0db1a7dba6dd92cb42f3440ef0b2f6d280cdbd0c
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <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,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * 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
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @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)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <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
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         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){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @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
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @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
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.el.select('.popover-content',true).first();
19659     },
19660     
19661     getAutoCreate : function(){
19662          
19663         var cfg = {
19664            cls : 'popover roo-dynamic shadow roo-popover',
19665            style: 'display:block',
19666            cn : [
19667                 {
19668                     cls : 'arrow'
19669                 },
19670                 {
19671                     cls : 'popover-inner ',
19672                     cn : [
19673                         {
19674                             tag: 'h3',
19675                             cls: 'popover-title popover-header',
19676                             html : this.title || ''
19677                         },
19678                         {
19679                             cls : 'popover-content popover-body'  + this.cls,
19680                             html : this.html || ''
19681                         }
19682                     ]
19683                     
19684                 }
19685            ]
19686         };
19687         
19688         return cfg;
19689     },
19690     /**
19691      * @param {string} the title
19692      */
19693     setTitle: function(str)
19694     {
19695         this.title = str;
19696         if (this.el) {
19697             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19698         }
19699         
19700     },
19701     /**
19702      * @param {string} the body content
19703      */
19704     setContent: function(str)
19705     {
19706         this.html = str;
19707         if (this.el) {
19708             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19709         }
19710         
19711     },
19712     // as it get's added to the bottom of the page.
19713     onRender : function(ct, position)
19714     {
19715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19716         if(!this.el){
19717             var cfg = Roo.apply({},  this.getAutoCreate());
19718             cfg.id = Roo.id();
19719             
19720             if (this.cls) {
19721                 cfg.cls += ' ' + this.cls;
19722             }
19723             if (this.style) {
19724                 cfg.style = this.style;
19725             }
19726             //Roo.log("adding to ");
19727             this.el = Roo.get(document.body).createChild(cfg, position);
19728 //            Roo.log(this.el);
19729         }
19730         
19731         var nitems = [];
19732         if(typeof(this.items) != 'undefined'){
19733             var items = this.items;
19734             delete this.items;
19735
19736             for(var i =0;i < items.length;i++) {
19737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19738             }
19739         }
19740
19741         this.items = nitems;
19742         
19743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     resizeMask : function()
19751     {
19752         this.maskEl.setSize(
19753             Roo.lib.Dom.getViewWidth(true),
19754             Roo.lib.Dom.getViewHeight(true)
19755         );
19756     },
19757     
19758     initEvents : function()
19759     {
19760         
19761         Roo.bootstrap.Popover.register(this);
19762         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19763         this.el.enableDisplayMode('block');
19764         this.el.hide();
19765         if (this.over === false && !this.parent()) {
19766             return; 
19767         }
19768         if (this.triggers === false) {
19769             return;
19770         }
19771          
19772         // support parent
19773         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19774         var triggers = this.trigger ? this.trigger.split(' ') : [];
19775         Roo.each(triggers, function(trigger) {
19776         
19777             if (trigger == 'click') {
19778                 on_el.on('click', this.toggle, this);
19779             } else if (trigger != 'manual') {
19780                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19781                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19782       
19783                 on_el.on(eventIn  ,this.enter, this);
19784                 on_el.on(eventOut, this.leave, this);
19785             }
19786         }, this);
19787         
19788     },
19789     
19790     
19791     // private
19792     timeout : null,
19793     hoverState : null,
19794     
19795     toggle : function () {
19796         this.hoverState == 'in' ? this.leave() : this.enter();
19797     },
19798     
19799     enter : function () {
19800         
19801         clearTimeout(this.timeout);
19802     
19803         this.hoverState = 'in';
19804     
19805         if (!this.delay || !this.delay.show) {
19806             this.show();
19807             return;
19808         }
19809         var _t = this;
19810         this.timeout = setTimeout(function () {
19811             if (_t.hoverState == 'in') {
19812                 _t.show();
19813             }
19814         }, this.delay.show)
19815     },
19816     
19817     leave : function() {
19818         clearTimeout(this.timeout);
19819     
19820         this.hoverState = 'out';
19821     
19822         if (!this.delay || !this.delay.hide) {
19823             this.hide();
19824             return;
19825         }
19826         var _t = this;
19827         this.timeout = setTimeout(function () {
19828             if (_t.hoverState == 'out') {
19829                 _t.hide();
19830             }
19831         }, this.delay.hide)
19832     },
19833     /**
19834      * Show the popover
19835      * @param {Roo.Element|string|false} - element to align and point to.
19836      */
19837     show : function (on_el)
19838     {
19839         
19840         on_el = on_el || false; // default to false
19841         if (!on_el) {
19842             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19843                 on_el = this.parent().el;
19844             } else if (this.over) {
19845                 Roo.get(this.over);
19846             }
19847             
19848         }
19849         
19850         if (!this.el) {
19851             this.render(document.body);
19852         }
19853         
19854         
19855         this.el.removeClass([
19856             'fade','top','bottom', 'left', 'right','in',
19857             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19858         ]);
19859         
19860         if (!this.title.length) {
19861             this.el.select('.popover-title',true).hide();
19862         }
19863         
19864         
19865         var placement = typeof this.placement == 'function' ?
19866             this.placement.call(this, this.el, on_el) :
19867             this.placement;
19868             
19869         /*
19870         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19871         
19872         // I think  'auto right' - but 
19873         
19874         var autoPlace = autoToken.test(placement);
19875         if (autoPlace) {
19876             placement = placement.replace(autoToken, '') || 'top';
19877         }
19878         */
19879         
19880         
19881         this.el.show();
19882         this.el.dom.style.display='block';
19883         
19884         //this.el.appendTo(on_el);
19885         
19886         var p = this.getPosition();
19887         var box = this.el.getBox();
19888         
19889         
19890         var align = Roo.bootstrap.Popover.alignment[placement];
19891         this.el.addClass(align[2]);
19892
19893 //        Roo.log(align);
19894
19895         if (on_el) {
19896             this.el.alignTo(on_el, align[0],align[1]);
19897         } else {
19898             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19899             var es = this.el.getSize();
19900             var x = Roo.lib.Dom.getViewWidth()/2;
19901             var y = Roo.lib.Dom.getViewHeight()/2;
19902             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19903             
19904         }
19905
19906         
19907         //var arrow = this.el.select('.arrow',true).first();
19908         //arrow.set(align[2], 
19909         
19910         this.el.addClass('in');
19911         
19912         
19913         if (this.el.hasClass('fade')) {
19914             // fade it?
19915         }
19916         
19917         this.hoverState = 'in';
19918         
19919         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19920         if (this.modal) {
19921             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19922             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19923             this.maskEl.dom.style.display = 'block';
19924             this.maskEl.addClass('show');
19925         }
19926         
19927         
19928         
19929         this.fireEvent('show', this);
19930         
19931     },
19932     hide : function()
19933     {
19934         this.el.setXY([0,0]);
19935         this.el.removeClass('in');
19936         this.el.hide();
19937         this.hoverState = null;
19938         this.maskEl.hide(); // always..
19939         this.fireEvent('hide', this);
19940     }
19941     
19942 });
19943
19944
19945 Roo.apply(Roo.bootstrap.Popover, {
19946
19947     alignment : {
19948         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19949         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19950         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19951         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19952     },
19953     
19954     zIndex : 20001,
19955
19956     clickHander : false,
19957     
19958
19959     onMouseDown : function(e)
19960     {
19961         if (!e.getTarget(".roo-popover")) {
19962             this.hideAll();
19963         }
19964          
19965     },
19966     
19967     popups : [],
19968     
19969     register : function(popup)
19970     {
19971         if (!Roo.bootstrap.Popover.clickHandler) {
19972             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19973         }
19974         // hide other popups.
19975         this.hideAll();
19976         this.popups.push(popup);
19977     },
19978     hideAll : function()
19979     {
19980         this.popups.forEach(function(p) {
19981             p.hide();
19982         });
19983     }
19984
19985 });/*
19986  * - LGPL
19987  *
19988  * Progress
19989  * 
19990  */
19991
19992 /**
19993  * @class Roo.bootstrap.Progress
19994  * @extends Roo.bootstrap.Component
19995  * Bootstrap Progress class
19996  * @cfg {Boolean} striped striped of the progress bar
19997  * @cfg {Boolean} active animated of the progress bar
19998  * 
19999  * 
20000  * @constructor
20001  * Create a new Progress
20002  * @param {Object} config The config object
20003  */
20004
20005 Roo.bootstrap.Progress = function(config){
20006     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20007 };
20008
20009 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20010     
20011     striped : false,
20012     active: false,
20013     
20014     getAutoCreate : function(){
20015         var cfg = {
20016             tag: 'div',
20017             cls: 'progress'
20018         };
20019         
20020         
20021         if(this.striped){
20022             cfg.cls += ' progress-striped';
20023         }
20024       
20025         if(this.active){
20026             cfg.cls += ' active';
20027         }
20028         
20029         
20030         return cfg;
20031     }
20032    
20033 });
20034
20035  
20036
20037  /*
20038  * - LGPL
20039  *
20040  * ProgressBar
20041  * 
20042  */
20043
20044 /**
20045  * @class Roo.bootstrap.ProgressBar
20046  * @extends Roo.bootstrap.Component
20047  * Bootstrap ProgressBar class
20048  * @cfg {Number} aria_valuenow aria-value now
20049  * @cfg {Number} aria_valuemin aria-value min
20050  * @cfg {Number} aria_valuemax aria-value max
20051  * @cfg {String} label label for the progress bar
20052  * @cfg {String} panel (success | info | warning | danger )
20053  * @cfg {String} role role of the progress bar
20054  * @cfg {String} sr_only text
20055  * 
20056  * 
20057  * @constructor
20058  * Create a new ProgressBar
20059  * @param {Object} config The config object
20060  */
20061
20062 Roo.bootstrap.ProgressBar = function(config){
20063     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20064 };
20065
20066 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20067     
20068     aria_valuenow : 0,
20069     aria_valuemin : 0,
20070     aria_valuemax : 100,
20071     label : false,
20072     panel : false,
20073     role : false,
20074     sr_only: false,
20075     
20076     getAutoCreate : function()
20077     {
20078         
20079         var cfg = {
20080             tag: 'div',
20081             cls: 'progress-bar',
20082             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20083         };
20084         
20085         if(this.sr_only){
20086             cfg.cn = {
20087                 tag: 'span',
20088                 cls: 'sr-only',
20089                 html: this.sr_only
20090             }
20091         }
20092         
20093         if(this.role){
20094             cfg.role = this.role;
20095         }
20096         
20097         if(this.aria_valuenow){
20098             cfg['aria-valuenow'] = this.aria_valuenow;
20099         }
20100         
20101         if(this.aria_valuemin){
20102             cfg['aria-valuemin'] = this.aria_valuemin;
20103         }
20104         
20105         if(this.aria_valuemax){
20106             cfg['aria-valuemax'] = this.aria_valuemax;
20107         }
20108         
20109         if(this.label && !this.sr_only){
20110             cfg.html = this.label;
20111         }
20112         
20113         if(this.panel){
20114             cfg.cls += ' progress-bar-' + this.panel;
20115         }
20116         
20117         return cfg;
20118     },
20119     
20120     update : function(aria_valuenow)
20121     {
20122         this.aria_valuenow = aria_valuenow;
20123         
20124         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20125     }
20126    
20127 });
20128
20129  
20130
20131  /*
20132  * - LGPL
20133  *
20134  * column
20135  * 
20136  */
20137
20138 /**
20139  * @class Roo.bootstrap.TabGroup
20140  * @extends Roo.bootstrap.Column
20141  * Bootstrap Column class
20142  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20143  * @cfg {Boolean} carousel true to make the group behave like a carousel
20144  * @cfg {Boolean} bullets show bullets for the panels
20145  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20146  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20147  * @cfg {Boolean} showarrow (true|false) show arrow default true
20148  * 
20149  * @constructor
20150  * Create a new TabGroup
20151  * @param {Object} config The config object
20152  */
20153
20154 Roo.bootstrap.TabGroup = function(config){
20155     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20156     if (!this.navId) {
20157         this.navId = Roo.id();
20158     }
20159     this.tabs = [];
20160     Roo.bootstrap.TabGroup.register(this);
20161     
20162 };
20163
20164 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20165     
20166     carousel : false,
20167     transition : false,
20168     bullets : 0,
20169     timer : 0,
20170     autoslide : false,
20171     slideFn : false,
20172     slideOnTouch : false,
20173     showarrow : true,
20174     
20175     getAutoCreate : function()
20176     {
20177         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20178         
20179         cfg.cls += ' tab-content';
20180         
20181         if (this.carousel) {
20182             cfg.cls += ' carousel slide';
20183             
20184             cfg.cn = [{
20185                cls : 'carousel-inner',
20186                cn : []
20187             }];
20188         
20189             if(this.bullets  && !Roo.isTouch){
20190                 
20191                 var bullets = {
20192                     cls : 'carousel-bullets',
20193                     cn : []
20194                 };
20195                
20196                 if(this.bullets_cls){
20197                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20198                 }
20199                 
20200                 bullets.cn.push({
20201                     cls : 'clear'
20202                 });
20203                 
20204                 cfg.cn[0].cn.push(bullets);
20205             }
20206             
20207             if(this.showarrow){
20208                 cfg.cn[0].cn.push({
20209                     tag : 'div',
20210                     class : 'carousel-arrow',
20211                     cn : [
20212                         {
20213                             tag : 'div',
20214                             class : 'carousel-prev',
20215                             cn : [
20216                                 {
20217                                     tag : 'i',
20218                                     class : 'fa fa-chevron-left'
20219                                 }
20220                             ]
20221                         },
20222                         {
20223                             tag : 'div',
20224                             class : 'carousel-next',
20225                             cn : [
20226                                 {
20227                                     tag : 'i',
20228                                     class : 'fa fa-chevron-right'
20229                                 }
20230                             ]
20231                         }
20232                     ]
20233                 });
20234             }
20235             
20236         }
20237         
20238         return cfg;
20239     },
20240     
20241     initEvents:  function()
20242     {
20243 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20244 //            this.el.on("touchstart", this.onTouchStart, this);
20245 //        }
20246         
20247         if(this.autoslide){
20248             var _this = this;
20249             
20250             this.slideFn = window.setInterval(function() {
20251                 _this.showPanelNext();
20252             }, this.timer);
20253         }
20254         
20255         if(this.showarrow){
20256             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20257             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20258         }
20259         
20260         
20261     },
20262     
20263 //    onTouchStart : function(e, el, o)
20264 //    {
20265 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20266 //            return;
20267 //        }
20268 //        
20269 //        this.showPanelNext();
20270 //    },
20271     
20272     
20273     getChildContainer : function()
20274     {
20275         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20276     },
20277     
20278     /**
20279     * register a Navigation item
20280     * @param {Roo.bootstrap.NavItem} the navitem to add
20281     */
20282     register : function(item)
20283     {
20284         this.tabs.push( item);
20285         item.navId = this.navId; // not really needed..
20286         this.addBullet();
20287     
20288     },
20289     
20290     getActivePanel : function()
20291     {
20292         var r = false;
20293         Roo.each(this.tabs, function(t) {
20294             if (t.active) {
20295                 r = t;
20296                 return false;
20297             }
20298             return null;
20299         });
20300         return r;
20301         
20302     },
20303     getPanelByName : function(n)
20304     {
20305         var r = false;
20306         Roo.each(this.tabs, function(t) {
20307             if (t.tabId == n) {
20308                 r = t;
20309                 return false;
20310             }
20311             return null;
20312         });
20313         return r;
20314     },
20315     indexOfPanel : function(p)
20316     {
20317         var r = false;
20318         Roo.each(this.tabs, function(t,i) {
20319             if (t.tabId == p.tabId) {
20320                 r = i;
20321                 return false;
20322             }
20323             return null;
20324         });
20325         return r;
20326     },
20327     /**
20328      * show a specific panel
20329      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20330      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20331      */
20332     showPanel : function (pan)
20333     {
20334         if(this.transition || typeof(pan) == 'undefined'){
20335             Roo.log("waiting for the transitionend");
20336             return false;
20337         }
20338         
20339         if (typeof(pan) == 'number') {
20340             pan = this.tabs[pan];
20341         }
20342         
20343         if (typeof(pan) == 'string') {
20344             pan = this.getPanelByName(pan);
20345         }
20346         
20347         var cur = this.getActivePanel();
20348         
20349         if(!pan || !cur){
20350             Roo.log('pan or acitve pan is undefined');
20351             return false;
20352         }
20353         
20354         if (pan.tabId == this.getActivePanel().tabId) {
20355             return true;
20356         }
20357         
20358         if (false === cur.fireEvent('beforedeactivate')) {
20359             return false;
20360         }
20361         
20362         if(this.bullets > 0 && !Roo.isTouch){
20363             this.setActiveBullet(this.indexOfPanel(pan));
20364         }
20365         
20366         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20367             
20368             //class="carousel-item carousel-item-next carousel-item-left"
20369             
20370             this.transition = true;
20371             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20372             var lr = dir == 'next' ? 'left' : 'right';
20373             pan.el.addClass(dir); // or prev
20374             pan.el.addClass('carousel-item-' + dir); // or prev
20375             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20376             cur.el.addClass(lr); // or right
20377             pan.el.addClass(lr);
20378             cur.el.addClass('carousel-item-' +lr); // or right
20379             pan.el.addClass('carousel-item-' +lr);
20380             
20381             
20382             var _this = this;
20383             cur.el.on('transitionend', function() {
20384                 Roo.log("trans end?");
20385                 
20386                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20387                 pan.setActive(true);
20388                 
20389                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20390                 cur.setActive(false);
20391                 
20392                 _this.transition = false;
20393                 
20394             }, this, { single:  true } );
20395             
20396             return true;
20397         }
20398         
20399         cur.setActive(false);
20400         pan.setActive(true);
20401         
20402         return true;
20403         
20404     },
20405     showPanelNext : function()
20406     {
20407         var i = this.indexOfPanel(this.getActivePanel());
20408         
20409         if (i >= this.tabs.length - 1 && !this.autoslide) {
20410             return;
20411         }
20412         
20413         if (i >= this.tabs.length - 1 && this.autoslide) {
20414             i = -1;
20415         }
20416         
20417         this.showPanel(this.tabs[i+1]);
20418     },
20419     
20420     showPanelPrev : function()
20421     {
20422         var i = this.indexOfPanel(this.getActivePanel());
20423         
20424         if (i  < 1 && !this.autoslide) {
20425             return;
20426         }
20427         
20428         if (i < 1 && this.autoslide) {
20429             i = this.tabs.length;
20430         }
20431         
20432         this.showPanel(this.tabs[i-1]);
20433     },
20434     
20435     
20436     addBullet: function()
20437     {
20438         if(!this.bullets || Roo.isTouch){
20439             return;
20440         }
20441         var ctr = this.el.select('.carousel-bullets',true).first();
20442         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20443         var bullet = ctr.createChild({
20444             cls : 'bullet bullet-' + i
20445         },ctr.dom.lastChild);
20446         
20447         
20448         var _this = this;
20449         
20450         bullet.on('click', (function(e, el, o, ii, t){
20451
20452             e.preventDefault();
20453
20454             this.showPanel(ii);
20455
20456             if(this.autoslide && this.slideFn){
20457                 clearInterval(this.slideFn);
20458                 this.slideFn = window.setInterval(function() {
20459                     _this.showPanelNext();
20460                 }, this.timer);
20461             }
20462
20463         }).createDelegate(this, [i, bullet], true));
20464                 
20465         
20466     },
20467      
20468     setActiveBullet : function(i)
20469     {
20470         if(Roo.isTouch){
20471             return;
20472         }
20473         
20474         Roo.each(this.el.select('.bullet', true).elements, function(el){
20475             el.removeClass('selected');
20476         });
20477
20478         var bullet = this.el.select('.bullet-' + i, true).first();
20479         
20480         if(!bullet){
20481             return;
20482         }
20483         
20484         bullet.addClass('selected');
20485     }
20486     
20487     
20488   
20489 });
20490
20491  
20492
20493  
20494  
20495 Roo.apply(Roo.bootstrap.TabGroup, {
20496     
20497     groups: {},
20498      /**
20499     * register a Navigation Group
20500     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20501     */
20502     register : function(navgrp)
20503     {
20504         this.groups[navgrp.navId] = navgrp;
20505         
20506     },
20507     /**
20508     * fetch a Navigation Group based on the navigation ID
20509     * if one does not exist , it will get created.
20510     * @param {string} the navgroup to add
20511     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20512     */
20513     get: function(navId) {
20514         if (typeof(this.groups[navId]) == 'undefined') {
20515             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20516         }
20517         return this.groups[navId] ;
20518     }
20519     
20520     
20521     
20522 });
20523
20524  /*
20525  * - LGPL
20526  *
20527  * TabPanel
20528  * 
20529  */
20530
20531 /**
20532  * @class Roo.bootstrap.TabPanel
20533  * @extends Roo.bootstrap.Component
20534  * Bootstrap TabPanel class
20535  * @cfg {Boolean} active panel active
20536  * @cfg {String} html panel content
20537  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20538  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20539  * @cfg {String} href click to link..
20540  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20541  * 
20542  * 
20543  * @constructor
20544  * Create a new TabPanel
20545  * @param {Object} config The config object
20546  */
20547
20548 Roo.bootstrap.TabPanel = function(config){
20549     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20550     this.addEvents({
20551         /**
20552              * @event changed
20553              * Fires when the active status changes
20554              * @param {Roo.bootstrap.TabPanel} this
20555              * @param {Boolean} state the new state
20556             
20557          */
20558         'changed': true,
20559         /**
20560              * @event beforedeactivate
20561              * Fires before a tab is de-activated - can be used to do validation on a form.
20562              * @param {Roo.bootstrap.TabPanel} this
20563              * @return {Boolean} false if there is an error
20564             
20565          */
20566         'beforedeactivate': true
20567      });
20568     
20569     this.tabId = this.tabId || Roo.id();
20570   
20571 };
20572
20573 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20574     
20575     active: false,
20576     html: false,
20577     tabId: false,
20578     navId : false,
20579     href : '',
20580     touchSlide : false,
20581     getAutoCreate : function(){
20582         
20583         
20584         var cfg = {
20585             tag: 'div',
20586             // item is needed for carousel - not sure if it has any effect otherwise
20587             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20588             html: this.html || ''
20589         };
20590         
20591         if(this.active){
20592             cfg.cls += ' active';
20593         }
20594         
20595         if(this.tabId){
20596             cfg.tabId = this.tabId;
20597         }
20598         
20599         
20600         
20601         return cfg;
20602     },
20603     
20604     initEvents:  function()
20605     {
20606         var p = this.parent();
20607         
20608         this.navId = this.navId || p.navId;
20609         
20610         if (typeof(this.navId) != 'undefined') {
20611             // not really needed.. but just in case.. parent should be a NavGroup.
20612             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20613             
20614             tg.register(this);
20615             
20616             var i = tg.tabs.length - 1;
20617             
20618             if(this.active && tg.bullets > 0 && i < tg.bullets){
20619                 tg.setActiveBullet(i);
20620             }
20621         }
20622         
20623         this.el.on('click', this.onClick, this);
20624         
20625         if(Roo.isTouch && this.touchSlide){
20626             this.el.on("touchstart", this.onTouchStart, this);
20627             this.el.on("touchmove", this.onTouchMove, this);
20628             this.el.on("touchend", this.onTouchEnd, this);
20629         }
20630         
20631     },
20632     
20633     onRender : function(ct, position)
20634     {
20635         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20636     },
20637     
20638     setActive : function(state)
20639     {
20640         Roo.log("panel - set active " + this.tabId + "=" + state);
20641         
20642         this.active = state;
20643         if (!state) {
20644             this.el.removeClass('active');
20645             
20646         } else  if (!this.el.hasClass('active')) {
20647             this.el.addClass('active');
20648         }
20649         
20650         this.fireEvent('changed', this, state);
20651     },
20652     
20653     onClick : function(e)
20654     {
20655         e.preventDefault();
20656         
20657         if(!this.href.length){
20658             return;
20659         }
20660         
20661         window.location.href = this.href;
20662     },
20663     
20664     startX : 0,
20665     startY : 0,
20666     endX : 0,
20667     endY : 0,
20668     swiping : false,
20669     
20670     onTouchStart : function(e)
20671     {
20672         this.swiping = false;
20673         
20674         this.startX = e.browserEvent.touches[0].clientX;
20675         this.startY = e.browserEvent.touches[0].clientY;
20676     },
20677     
20678     onTouchMove : function(e)
20679     {
20680         this.swiping = true;
20681         
20682         this.endX = e.browserEvent.touches[0].clientX;
20683         this.endY = e.browserEvent.touches[0].clientY;
20684     },
20685     
20686     onTouchEnd : function(e)
20687     {
20688         if(!this.swiping){
20689             this.onClick(e);
20690             return;
20691         }
20692         
20693         var tabGroup = this.parent();
20694         
20695         if(this.endX > this.startX){ // swiping right
20696             tabGroup.showPanelPrev();
20697             return;
20698         }
20699         
20700         if(this.startX > this.endX){ // swiping left
20701             tabGroup.showPanelNext();
20702             return;
20703         }
20704     }
20705     
20706     
20707 });
20708  
20709
20710  
20711
20712  /*
20713  * - LGPL
20714  *
20715  * DateField
20716  * 
20717  */
20718
20719 /**
20720  * @class Roo.bootstrap.DateField
20721  * @extends Roo.bootstrap.Input
20722  * Bootstrap DateField class
20723  * @cfg {Number} weekStart default 0
20724  * @cfg {String} viewMode default empty, (months|years)
20725  * @cfg {String} minViewMode default empty, (months|years)
20726  * @cfg {Number} startDate default -Infinity
20727  * @cfg {Number} endDate default Infinity
20728  * @cfg {Boolean} todayHighlight default false
20729  * @cfg {Boolean} todayBtn default false
20730  * @cfg {Boolean} calendarWeeks default false
20731  * @cfg {Object} daysOfWeekDisabled default empty
20732  * @cfg {Boolean} singleMode default false (true | false)
20733  * 
20734  * @cfg {Boolean} keyboardNavigation default true
20735  * @cfg {String} language default en
20736  * 
20737  * @constructor
20738  * Create a new DateField
20739  * @param {Object} config The config object
20740  */
20741
20742 Roo.bootstrap.DateField = function(config){
20743     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20744      this.addEvents({
20745             /**
20746              * @event show
20747              * Fires when this field show.
20748              * @param {Roo.bootstrap.DateField} this
20749              * @param {Mixed} date The date value
20750              */
20751             show : true,
20752             /**
20753              * @event show
20754              * Fires when this field hide.
20755              * @param {Roo.bootstrap.DateField} this
20756              * @param {Mixed} date The date value
20757              */
20758             hide : true,
20759             /**
20760              * @event select
20761              * Fires when select a date.
20762              * @param {Roo.bootstrap.DateField} this
20763              * @param {Mixed} date The date value
20764              */
20765             select : true,
20766             /**
20767              * @event beforeselect
20768              * Fires when before select a date.
20769              * @param {Roo.bootstrap.DateField} this
20770              * @param {Mixed} date The date value
20771              */
20772             beforeselect : true
20773         });
20774 };
20775
20776 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20777     
20778     /**
20779      * @cfg {String} format
20780      * The default date format string which can be overriden for localization support.  The format must be
20781      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20782      */
20783     format : "m/d/y",
20784     /**
20785      * @cfg {String} altFormats
20786      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20787      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20788      */
20789     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20790     
20791     weekStart : 0,
20792     
20793     viewMode : '',
20794     
20795     minViewMode : '',
20796     
20797     todayHighlight : false,
20798     
20799     todayBtn: false,
20800     
20801     language: 'en',
20802     
20803     keyboardNavigation: true,
20804     
20805     calendarWeeks: false,
20806     
20807     startDate: -Infinity,
20808     
20809     endDate: Infinity,
20810     
20811     daysOfWeekDisabled: [],
20812     
20813     _events: [],
20814     
20815     singleMode : false,
20816     
20817     UTCDate: function()
20818     {
20819         return new Date(Date.UTC.apply(Date, arguments));
20820     },
20821     
20822     UTCToday: function()
20823     {
20824         var today = new Date();
20825         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20826     },
20827     
20828     getDate: function() {
20829             var d = this.getUTCDate();
20830             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20831     },
20832     
20833     getUTCDate: function() {
20834             return this.date;
20835     },
20836     
20837     setDate: function(d) {
20838             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20839     },
20840     
20841     setUTCDate: function(d) {
20842             this.date = d;
20843             this.setValue(this.formatDate(this.date));
20844     },
20845         
20846     onRender: function(ct, position)
20847     {
20848         
20849         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20850         
20851         this.language = this.language || 'en';
20852         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20853         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20854         
20855         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20856         this.format = this.format || 'm/d/y';
20857         this.isInline = false;
20858         this.isInput = true;
20859         this.component = this.el.select('.add-on', true).first() || false;
20860         this.component = (this.component && this.component.length === 0) ? false : this.component;
20861         this.hasInput = this.component && this.inputEl().length;
20862         
20863         if (typeof(this.minViewMode === 'string')) {
20864             switch (this.minViewMode) {
20865                 case 'months':
20866                     this.minViewMode = 1;
20867                     break;
20868                 case 'years':
20869                     this.minViewMode = 2;
20870                     break;
20871                 default:
20872                     this.minViewMode = 0;
20873                     break;
20874             }
20875         }
20876         
20877         if (typeof(this.viewMode === 'string')) {
20878             switch (this.viewMode) {
20879                 case 'months':
20880                     this.viewMode = 1;
20881                     break;
20882                 case 'years':
20883                     this.viewMode = 2;
20884                     break;
20885                 default:
20886                     this.viewMode = 0;
20887                     break;
20888             }
20889         }
20890                 
20891         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20892         
20893 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20894         
20895         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20896         
20897         this.picker().on('mousedown', this.onMousedown, this);
20898         this.picker().on('click', this.onClick, this);
20899         
20900         this.picker().addClass('datepicker-dropdown');
20901         
20902         this.startViewMode = this.viewMode;
20903         
20904         if(this.singleMode){
20905             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20906                 v.setVisibilityMode(Roo.Element.DISPLAY);
20907                 v.hide();
20908             });
20909             
20910             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20911                 v.setStyle('width', '189px');
20912             });
20913         }
20914         
20915         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20916             if(!this.calendarWeeks){
20917                 v.remove();
20918                 return;
20919             }
20920             
20921             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20922             v.attr('colspan', function(i, val){
20923                 return parseInt(val) + 1;
20924             });
20925         });
20926                         
20927         
20928         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20929         
20930         this.setStartDate(this.startDate);
20931         this.setEndDate(this.endDate);
20932         
20933         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20934         
20935         this.fillDow();
20936         this.fillMonths();
20937         this.update();
20938         this.showMode();
20939         
20940         if(this.isInline) {
20941             this.showPopup();
20942         }
20943     },
20944     
20945     picker : function()
20946     {
20947         return this.pickerEl;
20948 //        return this.el.select('.datepicker', true).first();
20949     },
20950     
20951     fillDow: function()
20952     {
20953         var dowCnt = this.weekStart;
20954         
20955         var dow = {
20956             tag: 'tr',
20957             cn: [
20958                 
20959             ]
20960         };
20961         
20962         if(this.calendarWeeks){
20963             dow.cn.push({
20964                 tag: 'th',
20965                 cls: 'cw',
20966                 html: '&nbsp;'
20967             })
20968         }
20969         
20970         while (dowCnt < this.weekStart + 7) {
20971             dow.cn.push({
20972                 tag: 'th',
20973                 cls: 'dow',
20974                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20975             });
20976         }
20977         
20978         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20979     },
20980     
20981     fillMonths: function()
20982     {    
20983         var i = 0;
20984         var months = this.picker().select('>.datepicker-months td', true).first();
20985         
20986         months.dom.innerHTML = '';
20987         
20988         while (i < 12) {
20989             var month = {
20990                 tag: 'span',
20991                 cls: 'month',
20992                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20993             };
20994             
20995             months.createChild(month);
20996         }
20997         
20998     },
20999     
21000     update: function()
21001     {
21002         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;
21003         
21004         if (this.date < this.startDate) {
21005             this.viewDate = new Date(this.startDate);
21006         } else if (this.date > this.endDate) {
21007             this.viewDate = new Date(this.endDate);
21008         } else {
21009             this.viewDate = new Date(this.date);
21010         }
21011         
21012         this.fill();
21013     },
21014     
21015     fill: function() 
21016     {
21017         var d = new Date(this.viewDate),
21018                 year = d.getUTCFullYear(),
21019                 month = d.getUTCMonth(),
21020                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21021                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21022                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21023                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21024                 currentDate = this.date && this.date.valueOf(),
21025                 today = this.UTCToday();
21026         
21027         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21028         
21029 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21030         
21031 //        this.picker.select('>tfoot th.today').
21032 //                                              .text(dates[this.language].today)
21033 //                                              .toggle(this.todayBtn !== false);
21034     
21035         this.updateNavArrows();
21036         this.fillMonths();
21037                                                 
21038         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21039         
21040         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21041          
21042         prevMonth.setUTCDate(day);
21043         
21044         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21045         
21046         var nextMonth = new Date(prevMonth);
21047         
21048         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21049         
21050         nextMonth = nextMonth.valueOf();
21051         
21052         var fillMonths = false;
21053         
21054         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21055         
21056         while(prevMonth.valueOf() <= nextMonth) {
21057             var clsName = '';
21058             
21059             if (prevMonth.getUTCDay() === this.weekStart) {
21060                 if(fillMonths){
21061                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21062                 }
21063                     
21064                 fillMonths = {
21065                     tag: 'tr',
21066                     cn: []
21067                 };
21068                 
21069                 if(this.calendarWeeks){
21070                     // ISO 8601: First week contains first thursday.
21071                     // ISO also states week starts on Monday, but we can be more abstract here.
21072                     var
21073                     // Start of current week: based on weekstart/current date
21074                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21075                     // Thursday of this week
21076                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21077                     // First Thursday of year, year from thursday
21078                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21079                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21080                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21081                     
21082                     fillMonths.cn.push({
21083                         tag: 'td',
21084                         cls: 'cw',
21085                         html: calWeek
21086                     });
21087                 }
21088             }
21089             
21090             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21091                 clsName += ' old';
21092             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21093                 clsName += ' new';
21094             }
21095             if (this.todayHighlight &&
21096                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21097                 prevMonth.getUTCMonth() == today.getMonth() &&
21098                 prevMonth.getUTCDate() == today.getDate()) {
21099                 clsName += ' today';
21100             }
21101             
21102             if (currentDate && prevMonth.valueOf() === currentDate) {
21103                 clsName += ' active';
21104             }
21105             
21106             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21107                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21108                     clsName += ' disabled';
21109             }
21110             
21111             fillMonths.cn.push({
21112                 tag: 'td',
21113                 cls: 'day ' + clsName,
21114                 html: prevMonth.getDate()
21115             });
21116             
21117             prevMonth.setDate(prevMonth.getDate()+1);
21118         }
21119           
21120         var currentYear = this.date && this.date.getUTCFullYear();
21121         var currentMonth = this.date && this.date.getUTCMonth();
21122         
21123         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21124         
21125         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21126             v.removeClass('active');
21127             
21128             if(currentYear === year && k === currentMonth){
21129                 v.addClass('active');
21130             }
21131             
21132             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21133                 v.addClass('disabled');
21134             }
21135             
21136         });
21137         
21138         
21139         year = parseInt(year/10, 10) * 10;
21140         
21141         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21142         
21143         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21144         
21145         year -= 1;
21146         for (var i = -1; i < 11; i++) {
21147             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21148                 tag: 'span',
21149                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21150                 html: year
21151             });
21152             
21153             year += 1;
21154         }
21155     },
21156     
21157     showMode: function(dir) 
21158     {
21159         if (dir) {
21160             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21161         }
21162         
21163         Roo.each(this.picker().select('>div',true).elements, function(v){
21164             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21165             v.hide();
21166         });
21167         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21168     },
21169     
21170     place: function()
21171     {
21172         if(this.isInline) {
21173             return;
21174         }
21175         
21176         this.picker().removeClass(['bottom', 'top']);
21177         
21178         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21179             /*
21180              * place to the top of element!
21181              *
21182              */
21183             
21184             this.picker().addClass('top');
21185             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21186             
21187             return;
21188         }
21189         
21190         this.picker().addClass('bottom');
21191         
21192         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21193     },
21194     
21195     parseDate : function(value)
21196     {
21197         if(!value || value instanceof Date){
21198             return value;
21199         }
21200         var v = Date.parseDate(value, this.format);
21201         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21202             v = Date.parseDate(value, 'Y-m-d');
21203         }
21204         if(!v && this.altFormats){
21205             if(!this.altFormatsArray){
21206                 this.altFormatsArray = this.altFormats.split("|");
21207             }
21208             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21209                 v = Date.parseDate(value, this.altFormatsArray[i]);
21210             }
21211         }
21212         return v;
21213     },
21214     
21215     formatDate : function(date, fmt)
21216     {   
21217         return (!date || !(date instanceof Date)) ?
21218         date : date.dateFormat(fmt || this.format);
21219     },
21220     
21221     onFocus : function()
21222     {
21223         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21224         this.showPopup();
21225     },
21226     
21227     onBlur : function()
21228     {
21229         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21230         
21231         var d = this.inputEl().getValue();
21232         
21233         this.setValue(d);
21234                 
21235         this.hidePopup();
21236     },
21237     
21238     showPopup : function()
21239     {
21240         this.picker().show();
21241         this.update();
21242         this.place();
21243         
21244         this.fireEvent('showpopup', this, this.date);
21245     },
21246     
21247     hidePopup : function()
21248     {
21249         if(this.isInline) {
21250             return;
21251         }
21252         this.picker().hide();
21253         this.viewMode = this.startViewMode;
21254         this.showMode();
21255         
21256         this.fireEvent('hidepopup', this, this.date);
21257         
21258     },
21259     
21260     onMousedown: function(e)
21261     {
21262         e.stopPropagation();
21263         e.preventDefault();
21264     },
21265     
21266     keyup: function(e)
21267     {
21268         Roo.bootstrap.DateField.superclass.keyup.call(this);
21269         this.update();
21270     },
21271
21272     setValue: function(v)
21273     {
21274         if(this.fireEvent('beforeselect', this, v) !== false){
21275             var d = new Date(this.parseDate(v) ).clearTime();
21276         
21277             if(isNaN(d.getTime())){
21278                 this.date = this.viewDate = '';
21279                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21280                 return;
21281             }
21282
21283             v = this.formatDate(d);
21284
21285             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21286
21287             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21288
21289             this.update();
21290
21291             this.fireEvent('select', this, this.date);
21292         }
21293     },
21294     
21295     getValue: function()
21296     {
21297         return this.formatDate(this.date);
21298     },
21299     
21300     fireKey: function(e)
21301     {
21302         if (!this.picker().isVisible()){
21303             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21304                 this.showPopup();
21305             }
21306             return;
21307         }
21308         
21309         var dateChanged = false,
21310         dir, day, month,
21311         newDate, newViewDate;
21312         
21313         switch(e.keyCode){
21314             case 27: // escape
21315                 this.hidePopup();
21316                 e.preventDefault();
21317                 break;
21318             case 37: // left
21319             case 39: // right
21320                 if (!this.keyboardNavigation) {
21321                     break;
21322                 }
21323                 dir = e.keyCode == 37 ? -1 : 1;
21324                 
21325                 if (e.ctrlKey){
21326                     newDate = this.moveYear(this.date, dir);
21327                     newViewDate = this.moveYear(this.viewDate, dir);
21328                 } else if (e.shiftKey){
21329                     newDate = this.moveMonth(this.date, dir);
21330                     newViewDate = this.moveMonth(this.viewDate, dir);
21331                 } else {
21332                     newDate = new Date(this.date);
21333                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21334                     newViewDate = new Date(this.viewDate);
21335                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21336                 }
21337                 if (this.dateWithinRange(newDate)){
21338                     this.date = newDate;
21339                     this.viewDate = newViewDate;
21340                     this.setValue(this.formatDate(this.date));
21341 //                    this.update();
21342                     e.preventDefault();
21343                     dateChanged = true;
21344                 }
21345                 break;
21346             case 38: // up
21347             case 40: // down
21348                 if (!this.keyboardNavigation) {
21349                     break;
21350                 }
21351                 dir = e.keyCode == 38 ? -1 : 1;
21352                 if (e.ctrlKey){
21353                     newDate = this.moveYear(this.date, dir);
21354                     newViewDate = this.moveYear(this.viewDate, dir);
21355                 } else if (e.shiftKey){
21356                     newDate = this.moveMonth(this.date, dir);
21357                     newViewDate = this.moveMonth(this.viewDate, dir);
21358                 } else {
21359                     newDate = new Date(this.date);
21360                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21361                     newViewDate = new Date(this.viewDate);
21362                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21363                 }
21364                 if (this.dateWithinRange(newDate)){
21365                     this.date = newDate;
21366                     this.viewDate = newViewDate;
21367                     this.setValue(this.formatDate(this.date));
21368 //                    this.update();
21369                     e.preventDefault();
21370                     dateChanged = true;
21371                 }
21372                 break;
21373             case 13: // enter
21374                 this.setValue(this.formatDate(this.date));
21375                 this.hidePopup();
21376                 e.preventDefault();
21377                 break;
21378             case 9: // tab
21379                 this.setValue(this.formatDate(this.date));
21380                 this.hidePopup();
21381                 break;
21382             case 16: // shift
21383             case 17: // ctrl
21384             case 18: // alt
21385                 break;
21386             default :
21387                 this.hidePopup();
21388                 
21389         }
21390     },
21391     
21392     
21393     onClick: function(e) 
21394     {
21395         e.stopPropagation();
21396         e.preventDefault();
21397         
21398         var target = e.getTarget();
21399         
21400         if(target.nodeName.toLowerCase() === 'i'){
21401             target = Roo.get(target).dom.parentNode;
21402         }
21403         
21404         var nodeName = target.nodeName;
21405         var className = target.className;
21406         var html = target.innerHTML;
21407         //Roo.log(nodeName);
21408         
21409         switch(nodeName.toLowerCase()) {
21410             case 'th':
21411                 switch(className) {
21412                     case 'switch':
21413                         this.showMode(1);
21414                         break;
21415                     case 'prev':
21416                     case 'next':
21417                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21418                         switch(this.viewMode){
21419                                 case 0:
21420                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21421                                         break;
21422                                 case 1:
21423                                 case 2:
21424                                         this.viewDate = this.moveYear(this.viewDate, dir);
21425                                         break;
21426                         }
21427                         this.fill();
21428                         break;
21429                     case 'today':
21430                         var date = new Date();
21431                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21432 //                        this.fill()
21433                         this.setValue(this.formatDate(this.date));
21434                         
21435                         this.hidePopup();
21436                         break;
21437                 }
21438                 break;
21439             case 'span':
21440                 if (className.indexOf('disabled') < 0) {
21441                     this.viewDate.setUTCDate(1);
21442                     if (className.indexOf('month') > -1) {
21443                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21444                     } else {
21445                         var year = parseInt(html, 10) || 0;
21446                         this.viewDate.setUTCFullYear(year);
21447                         
21448                     }
21449                     
21450                     if(this.singleMode){
21451                         this.setValue(this.formatDate(this.viewDate));
21452                         this.hidePopup();
21453                         return;
21454                     }
21455                     
21456                     this.showMode(-1);
21457                     this.fill();
21458                 }
21459                 break;
21460                 
21461             case 'td':
21462                 //Roo.log(className);
21463                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21464                     var day = parseInt(html, 10) || 1;
21465                     var year = this.viewDate.getUTCFullYear(),
21466                         month = this.viewDate.getUTCMonth();
21467
21468                     if (className.indexOf('old') > -1) {
21469                         if(month === 0 ){
21470                             month = 11;
21471                             year -= 1;
21472                         }else{
21473                             month -= 1;
21474                         }
21475                     } else if (className.indexOf('new') > -1) {
21476                         if (month == 11) {
21477                             month = 0;
21478                             year += 1;
21479                         } else {
21480                             month += 1;
21481                         }
21482                     }
21483                     //Roo.log([year,month,day]);
21484                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21485                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21486 //                    this.fill();
21487                     //Roo.log(this.formatDate(this.date));
21488                     this.setValue(this.formatDate(this.date));
21489                     this.hidePopup();
21490                 }
21491                 break;
21492         }
21493     },
21494     
21495     setStartDate: function(startDate)
21496     {
21497         this.startDate = startDate || -Infinity;
21498         if (this.startDate !== -Infinity) {
21499             this.startDate = this.parseDate(this.startDate);
21500         }
21501         this.update();
21502         this.updateNavArrows();
21503     },
21504
21505     setEndDate: function(endDate)
21506     {
21507         this.endDate = endDate || Infinity;
21508         if (this.endDate !== Infinity) {
21509             this.endDate = this.parseDate(this.endDate);
21510         }
21511         this.update();
21512         this.updateNavArrows();
21513     },
21514     
21515     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21516     {
21517         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21518         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21519             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21520         }
21521         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21522             return parseInt(d, 10);
21523         });
21524         this.update();
21525         this.updateNavArrows();
21526     },
21527     
21528     updateNavArrows: function() 
21529     {
21530         if(this.singleMode){
21531             return;
21532         }
21533         
21534         var d = new Date(this.viewDate),
21535         year = d.getUTCFullYear(),
21536         month = d.getUTCMonth();
21537         
21538         Roo.each(this.picker().select('.prev', true).elements, function(v){
21539             v.show();
21540             switch (this.viewMode) {
21541                 case 0:
21542
21543                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21544                         v.hide();
21545                     }
21546                     break;
21547                 case 1:
21548                 case 2:
21549                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21550                         v.hide();
21551                     }
21552                     break;
21553             }
21554         });
21555         
21556         Roo.each(this.picker().select('.next', true).elements, function(v){
21557             v.show();
21558             switch (this.viewMode) {
21559                 case 0:
21560
21561                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21562                         v.hide();
21563                     }
21564                     break;
21565                 case 1:
21566                 case 2:
21567                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21568                         v.hide();
21569                     }
21570                     break;
21571             }
21572         })
21573     },
21574     
21575     moveMonth: function(date, dir)
21576     {
21577         if (!dir) {
21578             return date;
21579         }
21580         var new_date = new Date(date.valueOf()),
21581         day = new_date.getUTCDate(),
21582         month = new_date.getUTCMonth(),
21583         mag = Math.abs(dir),
21584         new_month, test;
21585         dir = dir > 0 ? 1 : -1;
21586         if (mag == 1){
21587             test = dir == -1
21588             // If going back one month, make sure month is not current month
21589             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21590             ? function(){
21591                 return new_date.getUTCMonth() == month;
21592             }
21593             // If going forward one month, make sure month is as expected
21594             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21595             : function(){
21596                 return new_date.getUTCMonth() != new_month;
21597             };
21598             new_month = month + dir;
21599             new_date.setUTCMonth(new_month);
21600             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21601             if (new_month < 0 || new_month > 11) {
21602                 new_month = (new_month + 12) % 12;
21603             }
21604         } else {
21605             // For magnitudes >1, move one month at a time...
21606             for (var i=0; i<mag; i++) {
21607                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21608                 new_date = this.moveMonth(new_date, dir);
21609             }
21610             // ...then reset the day, keeping it in the new month
21611             new_month = new_date.getUTCMonth();
21612             new_date.setUTCDate(day);
21613             test = function(){
21614                 return new_month != new_date.getUTCMonth();
21615             };
21616         }
21617         // Common date-resetting loop -- if date is beyond end of month, make it
21618         // end of month
21619         while (test()){
21620             new_date.setUTCDate(--day);
21621             new_date.setUTCMonth(new_month);
21622         }
21623         return new_date;
21624     },
21625
21626     moveYear: function(date, dir)
21627     {
21628         return this.moveMonth(date, dir*12);
21629     },
21630
21631     dateWithinRange: function(date)
21632     {
21633         return date >= this.startDate && date <= this.endDate;
21634     },
21635
21636     
21637     remove: function() 
21638     {
21639         this.picker().remove();
21640     },
21641     
21642     validateValue : function(value)
21643     {
21644         if(this.getVisibilityEl().hasClass('hidden')){
21645             return true;
21646         }
21647         
21648         if(value.length < 1)  {
21649             if(this.allowBlank){
21650                 return true;
21651             }
21652             return false;
21653         }
21654         
21655         if(value.length < this.minLength){
21656             return false;
21657         }
21658         if(value.length > this.maxLength){
21659             return false;
21660         }
21661         if(this.vtype){
21662             var vt = Roo.form.VTypes;
21663             if(!vt[this.vtype](value, this)){
21664                 return false;
21665             }
21666         }
21667         if(typeof this.validator == "function"){
21668             var msg = this.validator(value);
21669             if(msg !== true){
21670                 return false;
21671             }
21672         }
21673         
21674         if(this.regex && !this.regex.test(value)){
21675             return false;
21676         }
21677         
21678         if(typeof(this.parseDate(value)) == 'undefined'){
21679             return false;
21680         }
21681         
21682         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21683             return false;
21684         }      
21685         
21686         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21687             return false;
21688         } 
21689         
21690         
21691         return true;
21692     },
21693     
21694     reset : function()
21695     {
21696         this.date = this.viewDate = '';
21697         
21698         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21699     }
21700    
21701 });
21702
21703 Roo.apply(Roo.bootstrap.DateField,  {
21704     
21705     head : {
21706         tag: 'thead',
21707         cn: [
21708         {
21709             tag: 'tr',
21710             cn: [
21711             {
21712                 tag: 'th',
21713                 cls: 'prev',
21714                 html: '<i class="fa fa-arrow-left"/>'
21715             },
21716             {
21717                 tag: 'th',
21718                 cls: 'switch',
21719                 colspan: '5'
21720             },
21721             {
21722                 tag: 'th',
21723                 cls: 'next',
21724                 html: '<i class="fa fa-arrow-right"/>'
21725             }
21726
21727             ]
21728         }
21729         ]
21730     },
21731     
21732     content : {
21733         tag: 'tbody',
21734         cn: [
21735         {
21736             tag: 'tr',
21737             cn: [
21738             {
21739                 tag: 'td',
21740                 colspan: '7'
21741             }
21742             ]
21743         }
21744         ]
21745     },
21746     
21747     footer : {
21748         tag: 'tfoot',
21749         cn: [
21750         {
21751             tag: 'tr',
21752             cn: [
21753             {
21754                 tag: 'th',
21755                 colspan: '7',
21756                 cls: 'today'
21757             }
21758                     
21759             ]
21760         }
21761         ]
21762     },
21763     
21764     dates:{
21765         en: {
21766             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21767             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21768             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21769             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21770             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21771             today: "Today"
21772         }
21773     },
21774     
21775     modes: [
21776     {
21777         clsName: 'days',
21778         navFnc: 'Month',
21779         navStep: 1
21780     },
21781     {
21782         clsName: 'months',
21783         navFnc: 'FullYear',
21784         navStep: 1
21785     },
21786     {
21787         clsName: 'years',
21788         navFnc: 'FullYear',
21789         navStep: 10
21790     }]
21791 });
21792
21793 Roo.apply(Roo.bootstrap.DateField,  {
21794   
21795     template : {
21796         tag: 'div',
21797         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21798         cn: [
21799         {
21800             tag: 'div',
21801             cls: 'datepicker-days',
21802             cn: [
21803             {
21804                 tag: 'table',
21805                 cls: 'table-condensed',
21806                 cn:[
21807                 Roo.bootstrap.DateField.head,
21808                 {
21809                     tag: 'tbody'
21810                 },
21811                 Roo.bootstrap.DateField.footer
21812                 ]
21813             }
21814             ]
21815         },
21816         {
21817             tag: 'div',
21818             cls: 'datepicker-months',
21819             cn: [
21820             {
21821                 tag: 'table',
21822                 cls: 'table-condensed',
21823                 cn:[
21824                 Roo.bootstrap.DateField.head,
21825                 Roo.bootstrap.DateField.content,
21826                 Roo.bootstrap.DateField.footer
21827                 ]
21828             }
21829             ]
21830         },
21831         {
21832             tag: 'div',
21833             cls: 'datepicker-years',
21834             cn: [
21835             {
21836                 tag: 'table',
21837                 cls: 'table-condensed',
21838                 cn:[
21839                 Roo.bootstrap.DateField.head,
21840                 Roo.bootstrap.DateField.content,
21841                 Roo.bootstrap.DateField.footer
21842                 ]
21843             }
21844             ]
21845         }
21846         ]
21847     }
21848 });
21849
21850  
21851
21852  /*
21853  * - LGPL
21854  *
21855  * TimeField
21856  * 
21857  */
21858
21859 /**
21860  * @class Roo.bootstrap.TimeField
21861  * @extends Roo.bootstrap.Input
21862  * Bootstrap DateField class
21863  * 
21864  * 
21865  * @constructor
21866  * Create a new TimeField
21867  * @param {Object} config The config object
21868  */
21869
21870 Roo.bootstrap.TimeField = function(config){
21871     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21872     this.addEvents({
21873             /**
21874              * @event show
21875              * Fires when this field show.
21876              * @param {Roo.bootstrap.DateField} thisthis
21877              * @param {Mixed} date The date value
21878              */
21879             show : true,
21880             /**
21881              * @event show
21882              * Fires when this field hide.
21883              * @param {Roo.bootstrap.DateField} this
21884              * @param {Mixed} date The date value
21885              */
21886             hide : true,
21887             /**
21888              * @event select
21889              * Fires when select a date.
21890              * @param {Roo.bootstrap.DateField} this
21891              * @param {Mixed} date The date value
21892              */
21893             select : true
21894         });
21895 };
21896
21897 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21898     
21899     /**
21900      * @cfg {String} format
21901      * The default time format string which can be overriden for localization support.  The format must be
21902      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21903      */
21904     format : "H:i",
21905        
21906     onRender: function(ct, position)
21907     {
21908         
21909         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21910                 
21911         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21912         
21913         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21914         
21915         this.pop = this.picker().select('>.datepicker-time',true).first();
21916         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21917         
21918         this.picker().on('mousedown', this.onMousedown, this);
21919         this.picker().on('click', this.onClick, this);
21920         
21921         this.picker().addClass('datepicker-dropdown');
21922     
21923         this.fillTime();
21924         this.update();
21925             
21926         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21927         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21928         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21929         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21930         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21931         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21932
21933     },
21934     
21935     fireKey: function(e){
21936         if (!this.picker().isVisible()){
21937             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21938                 this.show();
21939             }
21940             return;
21941         }
21942
21943         e.preventDefault();
21944         
21945         switch(e.keyCode){
21946             case 27: // escape
21947                 this.hide();
21948                 break;
21949             case 37: // left
21950             case 39: // right
21951                 this.onTogglePeriod();
21952                 break;
21953             case 38: // up
21954                 this.onIncrementMinutes();
21955                 break;
21956             case 40: // down
21957                 this.onDecrementMinutes();
21958                 break;
21959             case 13: // enter
21960             case 9: // tab
21961                 this.setTime();
21962                 break;
21963         }
21964     },
21965     
21966     onClick: function(e) {
21967         e.stopPropagation();
21968         e.preventDefault();
21969     },
21970     
21971     picker : function()
21972     {
21973         return this.el.select('.datepicker', true).first();
21974     },
21975     
21976     fillTime: function()
21977     {    
21978         var time = this.pop.select('tbody', true).first();
21979         
21980         time.dom.innerHTML = '';
21981         
21982         time.createChild({
21983             tag: 'tr',
21984             cn: [
21985                 {
21986                     tag: 'td',
21987                     cn: [
21988                         {
21989                             tag: 'a',
21990                             href: '#',
21991                             cls: 'btn',
21992                             cn: [
21993                                 {
21994                                     tag: 'span',
21995                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21996                                 }
21997                             ]
21998                         } 
21999                     ]
22000                 },
22001                 {
22002                     tag: 'td',
22003                     cls: 'separator'
22004                 },
22005                 {
22006                     tag: 'td',
22007                     cn: [
22008                         {
22009                             tag: 'a',
22010                             href: '#',
22011                             cls: 'btn',
22012                             cn: [
22013                                 {
22014                                     tag: 'span',
22015                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22016                                 }
22017                             ]
22018                         }
22019                     ]
22020                 },
22021                 {
22022                     tag: 'td',
22023                     cls: 'separator'
22024                 }
22025             ]
22026         });
22027         
22028         time.createChild({
22029             tag: 'tr',
22030             cn: [
22031                 {
22032                     tag: 'td',
22033                     cn: [
22034                         {
22035                             tag: 'span',
22036                             cls: 'timepicker-hour',
22037                             html: '00'
22038                         }  
22039                     ]
22040                 },
22041                 {
22042                     tag: 'td',
22043                     cls: 'separator',
22044                     html: ':'
22045                 },
22046                 {
22047                     tag: 'td',
22048                     cn: [
22049                         {
22050                             tag: 'span',
22051                             cls: 'timepicker-minute',
22052                             html: '00'
22053                         }  
22054                     ]
22055                 },
22056                 {
22057                     tag: 'td',
22058                     cls: 'separator'
22059                 },
22060                 {
22061                     tag: 'td',
22062                     cn: [
22063                         {
22064                             tag: 'button',
22065                             type: 'button',
22066                             cls: 'btn btn-primary period',
22067                             html: 'AM'
22068                             
22069                         }
22070                     ]
22071                 }
22072             ]
22073         });
22074         
22075         time.createChild({
22076             tag: 'tr',
22077             cn: [
22078                 {
22079                     tag: 'td',
22080                     cn: [
22081                         {
22082                             tag: 'a',
22083                             href: '#',
22084                             cls: 'btn',
22085                             cn: [
22086                                 {
22087                                     tag: 'span',
22088                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22089                                 }
22090                             ]
22091                         }
22092                     ]
22093                 },
22094                 {
22095                     tag: 'td',
22096                     cls: 'separator'
22097                 },
22098                 {
22099                     tag: 'td',
22100                     cn: [
22101                         {
22102                             tag: 'a',
22103                             href: '#',
22104                             cls: 'btn',
22105                             cn: [
22106                                 {
22107                                     tag: 'span',
22108                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22109                                 }
22110                             ]
22111                         }
22112                     ]
22113                 },
22114                 {
22115                     tag: 'td',
22116                     cls: 'separator'
22117                 }
22118             ]
22119         });
22120         
22121     },
22122     
22123     update: function()
22124     {
22125         
22126         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22127         
22128         this.fill();
22129     },
22130     
22131     fill: function() 
22132     {
22133         var hours = this.time.getHours();
22134         var minutes = this.time.getMinutes();
22135         var period = 'AM';
22136         
22137         if(hours > 11){
22138             period = 'PM';
22139         }
22140         
22141         if(hours == 0){
22142             hours = 12;
22143         }
22144         
22145         
22146         if(hours > 12){
22147             hours = hours - 12;
22148         }
22149         
22150         if(hours < 10){
22151             hours = '0' + hours;
22152         }
22153         
22154         if(minutes < 10){
22155             minutes = '0' + minutes;
22156         }
22157         
22158         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22159         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22160         this.pop.select('button', true).first().dom.innerHTML = period;
22161         
22162     },
22163     
22164     place: function()
22165     {   
22166         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22167         
22168         var cls = ['bottom'];
22169         
22170         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22171             cls.pop();
22172             cls.push('top');
22173         }
22174         
22175         cls.push('right');
22176         
22177         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22178             cls.pop();
22179             cls.push('left');
22180         }
22181         
22182         this.picker().addClass(cls.join('-'));
22183         
22184         var _this = this;
22185         
22186         Roo.each(cls, function(c){
22187             if(c == 'bottom'){
22188                 _this.picker().setTop(_this.inputEl().getHeight());
22189                 return;
22190             }
22191             if(c == 'top'){
22192                 _this.picker().setTop(0 - _this.picker().getHeight());
22193                 return;
22194             }
22195             
22196             if(c == 'left'){
22197                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22198                 return;
22199             }
22200             if(c == 'right'){
22201                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22202                 return;
22203             }
22204         });
22205         
22206     },
22207   
22208     onFocus : function()
22209     {
22210         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22211         this.show();
22212     },
22213     
22214     onBlur : function()
22215     {
22216         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22217         this.hide();
22218     },
22219     
22220     show : function()
22221     {
22222         this.picker().show();
22223         this.pop.show();
22224         this.update();
22225         this.place();
22226         
22227         this.fireEvent('show', this, this.date);
22228     },
22229     
22230     hide : function()
22231     {
22232         this.picker().hide();
22233         this.pop.hide();
22234         
22235         this.fireEvent('hide', this, this.date);
22236     },
22237     
22238     setTime : function()
22239     {
22240         this.hide();
22241         this.setValue(this.time.format(this.format));
22242         
22243         this.fireEvent('select', this, this.date);
22244         
22245         
22246     },
22247     
22248     onMousedown: function(e){
22249         e.stopPropagation();
22250         e.preventDefault();
22251     },
22252     
22253     onIncrementHours: function()
22254     {
22255         Roo.log('onIncrementHours');
22256         this.time = this.time.add(Date.HOUR, 1);
22257         this.update();
22258         
22259     },
22260     
22261     onDecrementHours: function()
22262     {
22263         Roo.log('onDecrementHours');
22264         this.time = this.time.add(Date.HOUR, -1);
22265         this.update();
22266     },
22267     
22268     onIncrementMinutes: function()
22269     {
22270         Roo.log('onIncrementMinutes');
22271         this.time = this.time.add(Date.MINUTE, 1);
22272         this.update();
22273     },
22274     
22275     onDecrementMinutes: function()
22276     {
22277         Roo.log('onDecrementMinutes');
22278         this.time = this.time.add(Date.MINUTE, -1);
22279         this.update();
22280     },
22281     
22282     onTogglePeriod: function()
22283     {
22284         Roo.log('onTogglePeriod');
22285         this.time = this.time.add(Date.HOUR, 12);
22286         this.update();
22287     }
22288     
22289    
22290 });
22291
22292 Roo.apply(Roo.bootstrap.TimeField,  {
22293     
22294     content : {
22295         tag: 'tbody',
22296         cn: [
22297             {
22298                 tag: 'tr',
22299                 cn: [
22300                 {
22301                     tag: 'td',
22302                     colspan: '7'
22303                 }
22304                 ]
22305             }
22306         ]
22307     },
22308     
22309     footer : {
22310         tag: 'tfoot',
22311         cn: [
22312             {
22313                 tag: 'tr',
22314                 cn: [
22315                 {
22316                     tag: 'th',
22317                     colspan: '7',
22318                     cls: '',
22319                     cn: [
22320                         {
22321                             tag: 'button',
22322                             cls: 'btn btn-info ok',
22323                             html: 'OK'
22324                         }
22325                     ]
22326                 }
22327
22328                 ]
22329             }
22330         ]
22331     }
22332 });
22333
22334 Roo.apply(Roo.bootstrap.TimeField,  {
22335   
22336     template : {
22337         tag: 'div',
22338         cls: 'datepicker dropdown-menu',
22339         cn: [
22340             {
22341                 tag: 'div',
22342                 cls: 'datepicker-time',
22343                 cn: [
22344                 {
22345                     tag: 'table',
22346                     cls: 'table-condensed',
22347                     cn:[
22348                     Roo.bootstrap.TimeField.content,
22349                     Roo.bootstrap.TimeField.footer
22350                     ]
22351                 }
22352                 ]
22353             }
22354         ]
22355     }
22356 });
22357
22358  
22359
22360  /*
22361  * - LGPL
22362  *
22363  * MonthField
22364  * 
22365  */
22366
22367 /**
22368  * @class Roo.bootstrap.MonthField
22369  * @extends Roo.bootstrap.Input
22370  * Bootstrap MonthField class
22371  * 
22372  * @cfg {String} language default en
22373  * 
22374  * @constructor
22375  * Create a new MonthField
22376  * @param {Object} config The config object
22377  */
22378
22379 Roo.bootstrap.MonthField = function(config){
22380     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22381     
22382     this.addEvents({
22383         /**
22384          * @event show
22385          * Fires when this field show.
22386          * @param {Roo.bootstrap.MonthField} this
22387          * @param {Mixed} date The date value
22388          */
22389         show : true,
22390         /**
22391          * @event show
22392          * Fires when this field hide.
22393          * @param {Roo.bootstrap.MonthField} this
22394          * @param {Mixed} date The date value
22395          */
22396         hide : true,
22397         /**
22398          * @event select
22399          * Fires when select a date.
22400          * @param {Roo.bootstrap.MonthField} this
22401          * @param {String} oldvalue The old value
22402          * @param {String} newvalue The new value
22403          */
22404         select : true
22405     });
22406 };
22407
22408 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22409     
22410     onRender: function(ct, position)
22411     {
22412         
22413         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22414         
22415         this.language = this.language || 'en';
22416         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22417         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22418         
22419         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22420         this.isInline = false;
22421         this.isInput = true;
22422         this.component = this.el.select('.add-on', true).first() || false;
22423         this.component = (this.component && this.component.length === 0) ? false : this.component;
22424         this.hasInput = this.component && this.inputEL().length;
22425         
22426         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22427         
22428         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22429         
22430         this.picker().on('mousedown', this.onMousedown, this);
22431         this.picker().on('click', this.onClick, this);
22432         
22433         this.picker().addClass('datepicker-dropdown');
22434         
22435         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22436             v.setStyle('width', '189px');
22437         });
22438         
22439         this.fillMonths();
22440         
22441         this.update();
22442         
22443         if(this.isInline) {
22444             this.show();
22445         }
22446         
22447     },
22448     
22449     setValue: function(v, suppressEvent)
22450     {   
22451         var o = this.getValue();
22452         
22453         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22454         
22455         this.update();
22456
22457         if(suppressEvent !== true){
22458             this.fireEvent('select', this, o, v);
22459         }
22460         
22461     },
22462     
22463     getValue: function()
22464     {
22465         return this.value;
22466     },
22467     
22468     onClick: function(e) 
22469     {
22470         e.stopPropagation();
22471         e.preventDefault();
22472         
22473         var target = e.getTarget();
22474         
22475         if(target.nodeName.toLowerCase() === 'i'){
22476             target = Roo.get(target).dom.parentNode;
22477         }
22478         
22479         var nodeName = target.nodeName;
22480         var className = target.className;
22481         var html = target.innerHTML;
22482         
22483         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22484             return;
22485         }
22486         
22487         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22488         
22489         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22490         
22491         this.hide();
22492                         
22493     },
22494     
22495     picker : function()
22496     {
22497         return this.pickerEl;
22498     },
22499     
22500     fillMonths: function()
22501     {    
22502         var i = 0;
22503         var months = this.picker().select('>.datepicker-months td', true).first();
22504         
22505         months.dom.innerHTML = '';
22506         
22507         while (i < 12) {
22508             var month = {
22509                 tag: 'span',
22510                 cls: 'month',
22511                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22512             };
22513             
22514             months.createChild(month);
22515         }
22516         
22517     },
22518     
22519     update: function()
22520     {
22521         var _this = this;
22522         
22523         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22524             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22525         }
22526         
22527         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22528             e.removeClass('active');
22529             
22530             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22531                 e.addClass('active');
22532             }
22533         })
22534     },
22535     
22536     place: function()
22537     {
22538         if(this.isInline) {
22539             return;
22540         }
22541         
22542         this.picker().removeClass(['bottom', 'top']);
22543         
22544         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22545             /*
22546              * place to the top of element!
22547              *
22548              */
22549             
22550             this.picker().addClass('top');
22551             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22552             
22553             return;
22554         }
22555         
22556         this.picker().addClass('bottom');
22557         
22558         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22559     },
22560     
22561     onFocus : function()
22562     {
22563         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22564         this.show();
22565     },
22566     
22567     onBlur : function()
22568     {
22569         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22570         
22571         var d = this.inputEl().getValue();
22572         
22573         this.setValue(d);
22574                 
22575         this.hide();
22576     },
22577     
22578     show : function()
22579     {
22580         this.picker().show();
22581         this.picker().select('>.datepicker-months', true).first().show();
22582         this.update();
22583         this.place();
22584         
22585         this.fireEvent('show', this, this.date);
22586     },
22587     
22588     hide : function()
22589     {
22590         if(this.isInline) {
22591             return;
22592         }
22593         this.picker().hide();
22594         this.fireEvent('hide', this, this.date);
22595         
22596     },
22597     
22598     onMousedown: function(e)
22599     {
22600         e.stopPropagation();
22601         e.preventDefault();
22602     },
22603     
22604     keyup: function(e)
22605     {
22606         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22607         this.update();
22608     },
22609
22610     fireKey: function(e)
22611     {
22612         if (!this.picker().isVisible()){
22613             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22614                 this.show();
22615             }
22616             return;
22617         }
22618         
22619         var dir;
22620         
22621         switch(e.keyCode){
22622             case 27: // escape
22623                 this.hide();
22624                 e.preventDefault();
22625                 break;
22626             case 37: // left
22627             case 39: // right
22628                 dir = e.keyCode == 37 ? -1 : 1;
22629                 
22630                 this.vIndex = this.vIndex + dir;
22631                 
22632                 if(this.vIndex < 0){
22633                     this.vIndex = 0;
22634                 }
22635                 
22636                 if(this.vIndex > 11){
22637                     this.vIndex = 11;
22638                 }
22639                 
22640                 if(isNaN(this.vIndex)){
22641                     this.vIndex = 0;
22642                 }
22643                 
22644                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22645                 
22646                 break;
22647             case 38: // up
22648             case 40: // down
22649                 
22650                 dir = e.keyCode == 38 ? -1 : 1;
22651                 
22652                 this.vIndex = this.vIndex + dir * 4;
22653                 
22654                 if(this.vIndex < 0){
22655                     this.vIndex = 0;
22656                 }
22657                 
22658                 if(this.vIndex > 11){
22659                     this.vIndex = 11;
22660                 }
22661                 
22662                 if(isNaN(this.vIndex)){
22663                     this.vIndex = 0;
22664                 }
22665                 
22666                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22667                 break;
22668                 
22669             case 13: // enter
22670                 
22671                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22672                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22673                 }
22674                 
22675                 this.hide();
22676                 e.preventDefault();
22677                 break;
22678             case 9: // tab
22679                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22680                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22681                 }
22682                 this.hide();
22683                 break;
22684             case 16: // shift
22685             case 17: // ctrl
22686             case 18: // alt
22687                 break;
22688             default :
22689                 this.hide();
22690                 
22691         }
22692     },
22693     
22694     remove: function() 
22695     {
22696         this.picker().remove();
22697     }
22698    
22699 });
22700
22701 Roo.apply(Roo.bootstrap.MonthField,  {
22702     
22703     content : {
22704         tag: 'tbody',
22705         cn: [
22706         {
22707             tag: 'tr',
22708             cn: [
22709             {
22710                 tag: 'td',
22711                 colspan: '7'
22712             }
22713             ]
22714         }
22715         ]
22716     },
22717     
22718     dates:{
22719         en: {
22720             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22721             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22722         }
22723     }
22724 });
22725
22726 Roo.apply(Roo.bootstrap.MonthField,  {
22727   
22728     template : {
22729         tag: 'div',
22730         cls: 'datepicker dropdown-menu roo-dynamic',
22731         cn: [
22732             {
22733                 tag: 'div',
22734                 cls: 'datepicker-months',
22735                 cn: [
22736                 {
22737                     tag: 'table',
22738                     cls: 'table-condensed',
22739                     cn:[
22740                         Roo.bootstrap.DateField.content
22741                     ]
22742                 }
22743                 ]
22744             }
22745         ]
22746     }
22747 });
22748
22749  
22750
22751  
22752  /*
22753  * - LGPL
22754  *
22755  * CheckBox
22756  * 
22757  */
22758
22759 /**
22760  * @class Roo.bootstrap.CheckBox
22761  * @extends Roo.bootstrap.Input
22762  * Bootstrap CheckBox class
22763  * 
22764  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22765  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22766  * @cfg {String} boxLabel The text that appears beside the checkbox
22767  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22768  * @cfg {Boolean} checked initnal the element
22769  * @cfg {Boolean} inline inline the element (default false)
22770  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22771  * @cfg {String} tooltip label tooltip
22772  * 
22773  * @constructor
22774  * Create a new CheckBox
22775  * @param {Object} config The config object
22776  */
22777
22778 Roo.bootstrap.CheckBox = function(config){
22779     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22780    
22781     this.addEvents({
22782         /**
22783         * @event check
22784         * Fires when the element is checked or unchecked.
22785         * @param {Roo.bootstrap.CheckBox} this This input
22786         * @param {Boolean} checked The new checked value
22787         */
22788        check : true,
22789        /**
22790         * @event click
22791         * Fires when the element is click.
22792         * @param {Roo.bootstrap.CheckBox} this This input
22793         */
22794        click : true
22795     });
22796     
22797 };
22798
22799 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22800   
22801     inputType: 'checkbox',
22802     inputValue: 1,
22803     valueOff: 0,
22804     boxLabel: false,
22805     checked: false,
22806     weight : false,
22807     inline: false,
22808     tooltip : '',
22809     
22810     // checkbox success does not make any sense really.. 
22811     invalidClass : "",
22812     validClass : "",
22813     
22814     
22815     getAutoCreate : function()
22816     {
22817         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22818         
22819         var id = Roo.id();
22820         
22821         var cfg = {};
22822         
22823         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22824         
22825         if(this.inline){
22826             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22827         }
22828         
22829         var input =  {
22830             tag: 'input',
22831             id : id,
22832             type : this.inputType,
22833             value : this.inputValue,
22834             cls : 'roo-' + this.inputType, //'form-box',
22835             placeholder : this.placeholder || ''
22836             
22837         };
22838         
22839         if(this.inputType != 'radio'){
22840             var hidden =  {
22841                 tag: 'input',
22842                 type : 'hidden',
22843                 cls : 'roo-hidden-value',
22844                 value : this.checked ? this.inputValue : this.valueOff
22845             };
22846         }
22847         
22848             
22849         if (this.weight) { // Validity check?
22850             cfg.cls += " " + this.inputType + "-" + this.weight;
22851         }
22852         
22853         if (this.disabled) {
22854             input.disabled=true;
22855         }
22856         
22857         if(this.checked){
22858             input.checked = this.checked;
22859         }
22860         
22861         if (this.name) {
22862             
22863             input.name = this.name;
22864             
22865             if(this.inputType != 'radio'){
22866                 hidden.name = this.name;
22867                 input.name = '_hidden_' + this.name;
22868             }
22869         }
22870         
22871         if (this.size) {
22872             input.cls += ' input-' + this.size;
22873         }
22874         
22875         var settings=this;
22876         
22877         ['xs','sm','md','lg'].map(function(size){
22878             if (settings[size]) {
22879                 cfg.cls += ' col-' + size + '-' + settings[size];
22880             }
22881         });
22882         
22883         var inputblock = input;
22884          
22885         if (this.before || this.after) {
22886             
22887             inputblock = {
22888                 cls : 'input-group',
22889                 cn :  [] 
22890             };
22891             
22892             if (this.before) {
22893                 inputblock.cn.push({
22894                     tag :'span',
22895                     cls : 'input-group-addon',
22896                     html : this.before
22897                 });
22898             }
22899             
22900             inputblock.cn.push(input);
22901             
22902             if(this.inputType != 'radio'){
22903                 inputblock.cn.push(hidden);
22904             }
22905             
22906             if (this.after) {
22907                 inputblock.cn.push({
22908                     tag :'span',
22909                     cls : 'input-group-addon',
22910                     html : this.after
22911                 });
22912             }
22913             
22914         }
22915         var boxLabelCfg = false;
22916         
22917         if(this.boxLabel){
22918            
22919             boxLabelCfg = {
22920                 tag: 'label',
22921                 //'for': id, // box label is handled by onclick - so no for...
22922                 cls: 'box-label',
22923                 html: this.boxLabel
22924             };
22925             if(this.tooltip){
22926                 boxLabelCfg.tooltip = this.tooltip;
22927             }
22928              
22929         }
22930         
22931         
22932         if (align ==='left' && this.fieldLabel.length) {
22933 //                Roo.log("left and has label");
22934             cfg.cn = [
22935                 {
22936                     tag: 'label',
22937                     'for' :  id,
22938                     cls : 'control-label',
22939                     html : this.fieldLabel
22940                 },
22941                 {
22942                     cls : "", 
22943                     cn: [
22944                         inputblock
22945                     ]
22946                 }
22947             ];
22948             
22949             if (boxLabelCfg) {
22950                 cfg.cn[1].cn.push(boxLabelCfg);
22951             }
22952             
22953             if(this.labelWidth > 12){
22954                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22955             }
22956             
22957             if(this.labelWidth < 13 && this.labelmd == 0){
22958                 this.labelmd = this.labelWidth;
22959             }
22960             
22961             if(this.labellg > 0){
22962                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22963                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22964             }
22965             
22966             if(this.labelmd > 0){
22967                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22968                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22969             }
22970             
22971             if(this.labelsm > 0){
22972                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22973                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22974             }
22975             
22976             if(this.labelxs > 0){
22977                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22978                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22979             }
22980             
22981         } else if ( this.fieldLabel.length) {
22982 //                Roo.log(" label");
22983                 cfg.cn = [
22984                    
22985                     {
22986                         tag: this.boxLabel ? 'span' : 'label',
22987                         'for': id,
22988                         cls: 'control-label box-input-label',
22989                         //cls : 'input-group-addon',
22990                         html : this.fieldLabel
22991                     },
22992                     
22993                     inputblock
22994                     
22995                 ];
22996                 if (boxLabelCfg) {
22997                     cfg.cn.push(boxLabelCfg);
22998                 }
22999
23000         } else {
23001             
23002 //                Roo.log(" no label && no align");
23003                 cfg.cn = [  inputblock ] ;
23004                 if (boxLabelCfg) {
23005                     cfg.cn.push(boxLabelCfg);
23006                 }
23007
23008                 
23009         }
23010         
23011        
23012         
23013         if(this.inputType != 'radio'){
23014             cfg.cn.push(hidden);
23015         }
23016         
23017         return cfg;
23018         
23019     },
23020     
23021     /**
23022      * return the real input element.
23023      */
23024     inputEl: function ()
23025     {
23026         return this.el.select('input.roo-' + this.inputType,true).first();
23027     },
23028     hiddenEl: function ()
23029     {
23030         return this.el.select('input.roo-hidden-value',true).first();
23031     },
23032     
23033     labelEl: function()
23034     {
23035         return this.el.select('label.control-label',true).first();
23036     },
23037     /* depricated... */
23038     
23039     label: function()
23040     {
23041         return this.labelEl();
23042     },
23043     
23044     boxLabelEl: function()
23045     {
23046         return this.el.select('label.box-label',true).first();
23047     },
23048     
23049     initEvents : function()
23050     {
23051 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23052         
23053         this.inputEl().on('click', this.onClick,  this);
23054         
23055         if (this.boxLabel) { 
23056             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23057         }
23058         
23059         this.startValue = this.getValue();
23060         
23061         if(this.groupId){
23062             Roo.bootstrap.CheckBox.register(this);
23063         }
23064     },
23065     
23066     onClick : function(e)
23067     {   
23068         if(this.fireEvent('click', this, e) !== false){
23069             this.setChecked(!this.checked);
23070         }
23071         
23072     },
23073     
23074     setChecked : function(state,suppressEvent)
23075     {
23076         this.startValue = this.getValue();
23077
23078         if(this.inputType == 'radio'){
23079             
23080             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23081                 e.dom.checked = false;
23082             });
23083             
23084             this.inputEl().dom.checked = true;
23085             
23086             this.inputEl().dom.value = this.inputValue;
23087             
23088             if(suppressEvent !== true){
23089                 this.fireEvent('check', this, true);
23090             }
23091             
23092             this.validate();
23093             
23094             return;
23095         }
23096         
23097         this.checked = state;
23098         
23099         this.inputEl().dom.checked = state;
23100         
23101         
23102         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23103         
23104         if(suppressEvent !== true){
23105             this.fireEvent('check', this, state);
23106         }
23107         
23108         this.validate();
23109     },
23110     
23111     getValue : function()
23112     {
23113         if(this.inputType == 'radio'){
23114             return this.getGroupValue();
23115         }
23116         
23117         return this.hiddenEl().dom.value;
23118         
23119     },
23120     
23121     getGroupValue : function()
23122     {
23123         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23124             return '';
23125         }
23126         
23127         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23128     },
23129     
23130     setValue : function(v,suppressEvent)
23131     {
23132         if(this.inputType == 'radio'){
23133             this.setGroupValue(v, suppressEvent);
23134             return;
23135         }
23136         
23137         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23138         
23139         this.validate();
23140     },
23141     
23142     setGroupValue : function(v, suppressEvent)
23143     {
23144         this.startValue = this.getValue();
23145         
23146         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23147             e.dom.checked = false;
23148             
23149             if(e.dom.value == v){
23150                 e.dom.checked = true;
23151             }
23152         });
23153         
23154         if(suppressEvent !== true){
23155             this.fireEvent('check', this, true);
23156         }
23157
23158         this.validate();
23159         
23160         return;
23161     },
23162     
23163     validate : function()
23164     {
23165         if(this.getVisibilityEl().hasClass('hidden')){
23166             return true;
23167         }
23168         
23169         if(
23170                 this.disabled || 
23171                 (this.inputType == 'radio' && this.validateRadio()) ||
23172                 (this.inputType == 'checkbox' && this.validateCheckbox())
23173         ){
23174             this.markValid();
23175             return true;
23176         }
23177         
23178         this.markInvalid();
23179         return false;
23180     },
23181     
23182     validateRadio : function()
23183     {
23184         if(this.getVisibilityEl().hasClass('hidden')){
23185             return true;
23186         }
23187         
23188         if(this.allowBlank){
23189             return true;
23190         }
23191         
23192         var valid = false;
23193         
23194         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23195             if(!e.dom.checked){
23196                 return;
23197             }
23198             
23199             valid = true;
23200             
23201             return false;
23202         });
23203         
23204         return valid;
23205     },
23206     
23207     validateCheckbox : function()
23208     {
23209         if(!this.groupId){
23210             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23211             //return (this.getValue() == this.inputValue) ? true : false;
23212         }
23213         
23214         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23215         
23216         if(!group){
23217             return false;
23218         }
23219         
23220         var r = false;
23221         
23222         for(var i in group){
23223             if(group[i].el.isVisible(true)){
23224                 r = false;
23225                 break;
23226             }
23227             
23228             r = true;
23229         }
23230         
23231         for(var i in group){
23232             if(r){
23233                 break;
23234             }
23235             
23236             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23237         }
23238         
23239         return r;
23240     },
23241     
23242     /**
23243      * Mark this field as valid
23244      */
23245     markValid : function()
23246     {
23247         var _this = this;
23248         
23249         this.fireEvent('valid', this);
23250         
23251         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23252         
23253         if(this.groupId){
23254             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23255         }
23256         
23257         if(label){
23258             label.markValid();
23259         }
23260
23261         if(this.inputType == 'radio'){
23262             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23263                 var fg = e.findParent('.form-group', false, true);
23264                 if (Roo.bootstrap.version == 3) {
23265                     fg.removeClass([_this.invalidClass, _this.validClass]);
23266                     fg.addClass(_this.validClass);
23267                 } else {
23268                     fg.removeClass(['is-valid', 'is-invalid']);
23269                     fg.addClass('is-valid');
23270                 }
23271             });
23272             
23273             return;
23274         }
23275
23276         if(!this.groupId){
23277             var fg = this.el.findParent('.form-group', false, true);
23278             if (Roo.bootstrap.version == 3) {
23279                 fg.removeClass([this.invalidClass, this.validClass]);
23280                 fg.addClass(this.validClass);
23281             } else {
23282                 fg.removeClass(['is-valid', 'is-invalid']);
23283                 fg.addClass('is-valid');
23284             }
23285             return;
23286         }
23287         
23288         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23289         
23290         if(!group){
23291             return;
23292         }
23293         
23294         for(var i in group){
23295             var fg = group[i].el.findParent('.form-group', false, true);
23296             if (Roo.bootstrap.version == 3) {
23297                 fg.removeClass([this.invalidClass, this.validClass]);
23298                 fg.addClass(this.validClass);
23299             } else {
23300                 fg.removeClass(['is-valid', 'is-invalid']);
23301                 fg.addClass('is-valid');
23302             }
23303         }
23304     },
23305     
23306      /**
23307      * Mark this field as invalid
23308      * @param {String} msg The validation message
23309      */
23310     markInvalid : function(msg)
23311     {
23312         if(this.allowBlank){
23313             return;
23314         }
23315         
23316         var _this = this;
23317         
23318         this.fireEvent('invalid', this, msg);
23319         
23320         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23321         
23322         if(this.groupId){
23323             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23324         }
23325         
23326         if(label){
23327             label.markInvalid();
23328         }
23329             
23330         if(this.inputType == 'radio'){
23331             
23332             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23333                 var fg = e.findParent('.form-group', false, true);
23334                 if (Roo.bootstrap.version == 3) {
23335                     fg.removeClass([_this.invalidClass, _this.validClass]);
23336                     fg.addClass(_this.invalidClass);
23337                 } else {
23338                     fg.removeClass(['is-invalid', 'is-valid']);
23339                     fg.addClass('is-invalid');
23340                 }
23341             });
23342             
23343             return;
23344         }
23345         
23346         if(!this.groupId){
23347             var fg = this.el.findParent('.form-group', false, true);
23348             if (Roo.bootstrap.version == 3) {
23349                 fg.removeClass([_this.invalidClass, _this.validClass]);
23350                 fg.addClass(_this.invalidClass);
23351             } else {
23352                 fg.removeClass(['is-invalid', 'is-valid']);
23353                 fg.addClass('is-invalid');
23354             }
23355             return;
23356         }
23357         
23358         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23359         
23360         if(!group){
23361             return;
23362         }
23363         
23364         for(var i in group){
23365             var fg = group[i].el.findParent('.form-group', false, true);
23366             if (Roo.bootstrap.version == 3) {
23367                 fg.removeClass([_this.invalidClass, _this.validClass]);
23368                 fg.addClass(_this.invalidClass);
23369             } else {
23370                 fg.removeClass(['is-invalid', 'is-valid']);
23371                 fg.addClass('is-invalid');
23372             }
23373         }
23374         
23375     },
23376     
23377     clearInvalid : function()
23378     {
23379         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23380         
23381         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23382         
23383         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23384         
23385         if (label && label.iconEl) {
23386             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23387             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23388         }
23389     },
23390     
23391     disable : function()
23392     {
23393         if(this.inputType != 'radio'){
23394             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23395             return;
23396         }
23397         
23398         var _this = this;
23399         
23400         if(this.rendered){
23401             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23402                 _this.getActionEl().addClass(this.disabledClass);
23403                 e.dom.disabled = true;
23404             });
23405         }
23406         
23407         this.disabled = true;
23408         this.fireEvent("disable", this);
23409         return this;
23410     },
23411
23412     enable : function()
23413     {
23414         if(this.inputType != 'radio'){
23415             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23416             return;
23417         }
23418         
23419         var _this = this;
23420         
23421         if(this.rendered){
23422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23423                 _this.getActionEl().removeClass(this.disabledClass);
23424                 e.dom.disabled = false;
23425             });
23426         }
23427         
23428         this.disabled = false;
23429         this.fireEvent("enable", this);
23430         return this;
23431     },
23432     
23433     setBoxLabel : function(v)
23434     {
23435         this.boxLabel = v;
23436         
23437         if(this.rendered){
23438             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23439         }
23440     }
23441
23442 });
23443
23444 Roo.apply(Roo.bootstrap.CheckBox, {
23445     
23446     groups: {},
23447     
23448      /**
23449     * register a CheckBox Group
23450     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23451     */
23452     register : function(checkbox)
23453     {
23454         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23455             this.groups[checkbox.groupId] = {};
23456         }
23457         
23458         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23459             return;
23460         }
23461         
23462         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23463         
23464     },
23465     /**
23466     * fetch a CheckBox Group based on the group ID
23467     * @param {string} the group ID
23468     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23469     */
23470     get: function(groupId) {
23471         if (typeof(this.groups[groupId]) == 'undefined') {
23472             return false;
23473         }
23474         
23475         return this.groups[groupId] ;
23476     }
23477     
23478     
23479 });
23480 /*
23481  * - LGPL
23482  *
23483  * RadioItem
23484  * 
23485  */
23486
23487 /**
23488  * @class Roo.bootstrap.Radio
23489  * @extends Roo.bootstrap.Component
23490  * Bootstrap Radio class
23491  * @cfg {String} boxLabel - the label associated
23492  * @cfg {String} value - the value of radio
23493  * 
23494  * @constructor
23495  * Create a new Radio
23496  * @param {Object} config The config object
23497  */
23498 Roo.bootstrap.Radio = function(config){
23499     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23500     
23501 };
23502
23503 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23504     
23505     boxLabel : '',
23506     
23507     value : '',
23508     
23509     getAutoCreate : function()
23510     {
23511         var cfg = {
23512             tag : 'div',
23513             cls : 'form-group radio',
23514             cn : [
23515                 {
23516                     tag : 'label',
23517                     cls : 'box-label',
23518                     html : this.boxLabel
23519                 }
23520             ]
23521         };
23522         
23523         return cfg;
23524     },
23525     
23526     initEvents : function() 
23527     {
23528         this.parent().register(this);
23529         
23530         this.el.on('click', this.onClick, this);
23531         
23532     },
23533     
23534     onClick : function(e)
23535     {
23536         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23537             this.setChecked(true);
23538         }
23539     },
23540     
23541     setChecked : function(state, suppressEvent)
23542     {
23543         this.parent().setValue(this.value, suppressEvent);
23544         
23545     },
23546     
23547     setBoxLabel : function(v)
23548     {
23549         this.boxLabel = v;
23550         
23551         if(this.rendered){
23552             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23553         }
23554     }
23555     
23556 });
23557  
23558
23559  /*
23560  * - LGPL
23561  *
23562  * Input
23563  * 
23564  */
23565
23566 /**
23567  * @class Roo.bootstrap.SecurePass
23568  * @extends Roo.bootstrap.Input
23569  * Bootstrap SecurePass class
23570  *
23571  * 
23572  * @constructor
23573  * Create a new SecurePass
23574  * @param {Object} config The config object
23575  */
23576  
23577 Roo.bootstrap.SecurePass = function (config) {
23578     // these go here, so the translation tool can replace them..
23579     this.errors = {
23580         PwdEmpty: "Please type a password, and then retype it to confirm.",
23581         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23582         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23583         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23584         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23585         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23586         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23587         TooWeak: "Your password is Too Weak."
23588     },
23589     this.meterLabel = "Password strength:";
23590     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23591     this.meterClass = [
23592         "roo-password-meter-tooweak", 
23593         "roo-password-meter-weak", 
23594         "roo-password-meter-medium", 
23595         "roo-password-meter-strong", 
23596         "roo-password-meter-grey"
23597     ];
23598     
23599     this.errors = {};
23600     
23601     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23602 }
23603
23604 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23605     /**
23606      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23607      * {
23608      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23609      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23610      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23611      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23612      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23613      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23614      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23615      * })
23616      */
23617     // private
23618     
23619     meterWidth: 300,
23620     errorMsg :'',    
23621     errors: false,
23622     imageRoot: '/',
23623     /**
23624      * @cfg {String/Object} Label for the strength meter (defaults to
23625      * 'Password strength:')
23626      */
23627     // private
23628     meterLabel: '',
23629     /**
23630      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23631      * ['Weak', 'Medium', 'Strong'])
23632      */
23633     // private    
23634     pwdStrengths: false,    
23635     // private
23636     strength: 0,
23637     // private
23638     _lastPwd: null,
23639     // private
23640     kCapitalLetter: 0,
23641     kSmallLetter: 1,
23642     kDigit: 2,
23643     kPunctuation: 3,
23644     
23645     insecure: false,
23646     // private
23647     initEvents: function ()
23648     {
23649         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23650
23651         if (this.el.is('input[type=password]') && Roo.isSafari) {
23652             this.el.on('keydown', this.SafariOnKeyDown, this);
23653         }
23654
23655         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23656     },
23657     // private
23658     onRender: function (ct, position)
23659     {
23660         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23661         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23662         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23663
23664         this.trigger.createChild({
23665                    cn: [
23666                     {
23667                     //id: 'PwdMeter',
23668                     tag: 'div',
23669                     cls: 'roo-password-meter-grey col-xs-12',
23670                     style: {
23671                         //width: 0,
23672                         //width: this.meterWidth + 'px'                                                
23673                         }
23674                     },
23675                     {                            
23676                          cls: 'roo-password-meter-text'                          
23677                     }
23678                 ]            
23679         });
23680
23681          
23682         if (this.hideTrigger) {
23683             this.trigger.setDisplayed(false);
23684         }
23685         this.setSize(this.width || '', this.height || '');
23686     },
23687     // private
23688     onDestroy: function ()
23689     {
23690         if (this.trigger) {
23691             this.trigger.removeAllListeners();
23692             this.trigger.remove();
23693         }
23694         if (this.wrap) {
23695             this.wrap.remove();
23696         }
23697         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23698     },
23699     // private
23700     checkStrength: function ()
23701     {
23702         var pwd = this.inputEl().getValue();
23703         if (pwd == this._lastPwd) {
23704             return;
23705         }
23706
23707         var strength;
23708         if (this.ClientSideStrongPassword(pwd)) {
23709             strength = 3;
23710         } else if (this.ClientSideMediumPassword(pwd)) {
23711             strength = 2;
23712         } else if (this.ClientSideWeakPassword(pwd)) {
23713             strength = 1;
23714         } else {
23715             strength = 0;
23716         }
23717         
23718         Roo.log('strength1: ' + strength);
23719         
23720         //var pm = this.trigger.child('div/div/div').dom;
23721         var pm = this.trigger.child('div/div');
23722         pm.removeClass(this.meterClass);
23723         pm.addClass(this.meterClass[strength]);
23724                 
23725         
23726         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23727                 
23728         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23729         
23730         this._lastPwd = pwd;
23731     },
23732     reset: function ()
23733     {
23734         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23735         
23736         this._lastPwd = '';
23737         
23738         var pm = this.trigger.child('div/div');
23739         pm.removeClass(this.meterClass);
23740         pm.addClass('roo-password-meter-grey');        
23741         
23742         
23743         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23744         
23745         pt.innerHTML = '';
23746         this.inputEl().dom.type='password';
23747     },
23748     // private
23749     validateValue: function (value)
23750     {
23751         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23752             return false;
23753         }
23754         if (value.length == 0) {
23755             if (this.allowBlank) {
23756                 this.clearInvalid();
23757                 return true;
23758             }
23759
23760             this.markInvalid(this.errors.PwdEmpty);
23761             this.errorMsg = this.errors.PwdEmpty;
23762             return false;
23763         }
23764         
23765         if(this.insecure){
23766             return true;
23767         }
23768         
23769         if (!value.match(/[\x21-\x7e]+/)) {
23770             this.markInvalid(this.errors.PwdBadChar);
23771             this.errorMsg = this.errors.PwdBadChar;
23772             return false;
23773         }
23774         if (value.length < 6) {
23775             this.markInvalid(this.errors.PwdShort);
23776             this.errorMsg = this.errors.PwdShort;
23777             return false;
23778         }
23779         if (value.length > 16) {
23780             this.markInvalid(this.errors.PwdLong);
23781             this.errorMsg = this.errors.PwdLong;
23782             return false;
23783         }
23784         var strength;
23785         if (this.ClientSideStrongPassword(value)) {
23786             strength = 3;
23787         } else if (this.ClientSideMediumPassword(value)) {
23788             strength = 2;
23789         } else if (this.ClientSideWeakPassword(value)) {
23790             strength = 1;
23791         } else {
23792             strength = 0;
23793         }
23794
23795         
23796         if (strength < 2) {
23797             //this.markInvalid(this.errors.TooWeak);
23798             this.errorMsg = this.errors.TooWeak;
23799             //return false;
23800         }
23801         
23802         
23803         console.log('strength2: ' + strength);
23804         
23805         //var pm = this.trigger.child('div/div/div').dom;
23806         
23807         var pm = this.trigger.child('div/div');
23808         pm.removeClass(this.meterClass);
23809         pm.addClass(this.meterClass[strength]);
23810                 
23811         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23812                 
23813         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23814         
23815         this.errorMsg = ''; 
23816         return true;
23817     },
23818     // private
23819     CharacterSetChecks: function (type)
23820     {
23821         this.type = type;
23822         this.fResult = false;
23823     },
23824     // private
23825     isctype: function (character, type)
23826     {
23827         switch (type) {  
23828             case this.kCapitalLetter:
23829                 if (character >= 'A' && character <= 'Z') {
23830                     return true;
23831                 }
23832                 break;
23833             
23834             case this.kSmallLetter:
23835                 if (character >= 'a' && character <= 'z') {
23836                     return true;
23837                 }
23838                 break;
23839             
23840             case this.kDigit:
23841                 if (character >= '0' && character <= '9') {
23842                     return true;
23843                 }
23844                 break;
23845             
23846             case this.kPunctuation:
23847                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23848                     return true;
23849                 }
23850                 break;
23851             
23852             default:
23853                 return false;
23854         }
23855
23856     },
23857     // private
23858     IsLongEnough: function (pwd, size)
23859     {
23860         return !(pwd == null || isNaN(size) || pwd.length < size);
23861     },
23862     // private
23863     SpansEnoughCharacterSets: function (word, nb)
23864     {
23865         if (!this.IsLongEnough(word, nb))
23866         {
23867             return false;
23868         }
23869
23870         var characterSetChecks = new Array(
23871             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23872             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23873         );
23874         
23875         for (var index = 0; index < word.length; ++index) {
23876             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23877                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23878                     characterSetChecks[nCharSet].fResult = true;
23879                     break;
23880                 }
23881             }
23882         }
23883
23884         var nCharSets = 0;
23885         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23886             if (characterSetChecks[nCharSet].fResult) {
23887                 ++nCharSets;
23888             }
23889         }
23890
23891         if (nCharSets < nb) {
23892             return false;
23893         }
23894         return true;
23895     },
23896     // private
23897     ClientSideStrongPassword: function (pwd)
23898     {
23899         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23900     },
23901     // private
23902     ClientSideMediumPassword: function (pwd)
23903     {
23904         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23905     },
23906     // private
23907     ClientSideWeakPassword: function (pwd)
23908     {
23909         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23910     }
23911           
23912 })//<script type="text/javascript">
23913
23914 /*
23915  * Based  Ext JS Library 1.1.1
23916  * Copyright(c) 2006-2007, Ext JS, LLC.
23917  * LGPL
23918  *
23919  */
23920  
23921 /**
23922  * @class Roo.HtmlEditorCore
23923  * @extends Roo.Component
23924  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23925  *
23926  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23927  */
23928
23929 Roo.HtmlEditorCore = function(config){
23930     
23931     
23932     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23933     
23934     
23935     this.addEvents({
23936         /**
23937          * @event initialize
23938          * Fires when the editor is fully initialized (including the iframe)
23939          * @param {Roo.HtmlEditorCore} this
23940          */
23941         initialize: true,
23942         /**
23943          * @event activate
23944          * Fires when the editor is first receives the focus. Any insertion must wait
23945          * until after this event.
23946          * @param {Roo.HtmlEditorCore} this
23947          */
23948         activate: true,
23949          /**
23950          * @event beforesync
23951          * Fires before the textarea is updated with content from the editor iframe. Return false
23952          * to cancel the sync.
23953          * @param {Roo.HtmlEditorCore} this
23954          * @param {String} html
23955          */
23956         beforesync: true,
23957          /**
23958          * @event beforepush
23959          * Fires before the iframe editor is updated with content from the textarea. Return false
23960          * to cancel the push.
23961          * @param {Roo.HtmlEditorCore} this
23962          * @param {String} html
23963          */
23964         beforepush: true,
23965          /**
23966          * @event sync
23967          * Fires when the textarea is updated with content from the editor iframe.
23968          * @param {Roo.HtmlEditorCore} this
23969          * @param {String} html
23970          */
23971         sync: true,
23972          /**
23973          * @event push
23974          * Fires when the iframe editor is updated with content from the textarea.
23975          * @param {Roo.HtmlEditorCore} this
23976          * @param {String} html
23977          */
23978         push: true,
23979         
23980         /**
23981          * @event editorevent
23982          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23983          * @param {Roo.HtmlEditorCore} this
23984          */
23985         editorevent: true
23986         
23987     });
23988     
23989     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23990     
23991     // defaults : white / black...
23992     this.applyBlacklists();
23993     
23994     
23995     
23996 };
23997
23998
23999 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24000
24001
24002      /**
24003      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24004      */
24005     
24006     owner : false,
24007     
24008      /**
24009      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24010      *                        Roo.resizable.
24011      */
24012     resizable : false,
24013      /**
24014      * @cfg {Number} height (in pixels)
24015      */   
24016     height: 300,
24017    /**
24018      * @cfg {Number} width (in pixels)
24019      */   
24020     width: 500,
24021     
24022     /**
24023      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24024      * 
24025      */
24026     stylesheets: false,
24027     
24028     // id of frame..
24029     frameId: false,
24030     
24031     // private properties
24032     validationEvent : false,
24033     deferHeight: true,
24034     initialized : false,
24035     activated : false,
24036     sourceEditMode : false,
24037     onFocus : Roo.emptyFn,
24038     iframePad:3,
24039     hideMode:'offsets',
24040     
24041     clearUp: true,
24042     
24043     // blacklist + whitelisted elements..
24044     black: false,
24045     white: false,
24046      
24047     bodyCls : '',
24048
24049     /**
24050      * Protected method that will not generally be called directly. It
24051      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24052      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24053      */
24054     getDocMarkup : function(){
24055         // body styles..
24056         var st = '';
24057         
24058         // inherit styels from page...?? 
24059         if (this.stylesheets === false) {
24060             
24061             Roo.get(document.head).select('style').each(function(node) {
24062                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24063             });
24064             
24065             Roo.get(document.head).select('link').each(function(node) { 
24066                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24067             });
24068             
24069         } else if (!this.stylesheets.length) {
24070                 // simple..
24071                 st = '<style type="text/css">' +
24072                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24073                    '</style>';
24074         } else {
24075             for (var i in this.stylesheets) { 
24076                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24077             }
24078             
24079         }
24080         
24081         st +=  '<style type="text/css">' +
24082             'IMG { cursor: pointer } ' +
24083         '</style>';
24084
24085         var cls = 'roo-htmleditor-body';
24086         
24087         if(this.bodyCls.length){
24088             cls += ' ' + this.bodyCls;
24089         }
24090         
24091         return '<html><head>' + st  +
24092             //<style type="text/css">' +
24093             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24094             //'</style>' +
24095             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24096     },
24097
24098     // private
24099     onRender : function(ct, position)
24100     {
24101         var _t = this;
24102         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24103         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24104         
24105         
24106         this.el.dom.style.border = '0 none';
24107         this.el.dom.setAttribute('tabIndex', -1);
24108         this.el.addClass('x-hidden hide');
24109         
24110         
24111         
24112         if(Roo.isIE){ // fix IE 1px bogus margin
24113             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24114         }
24115        
24116         
24117         this.frameId = Roo.id();
24118         
24119          
24120         
24121         var iframe = this.owner.wrap.createChild({
24122             tag: 'iframe',
24123             cls: 'form-control', // bootstrap..
24124             id: this.frameId,
24125             name: this.frameId,
24126             frameBorder : 'no',
24127             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24128         }, this.el
24129         );
24130         
24131         
24132         this.iframe = iframe.dom;
24133
24134          this.assignDocWin();
24135         
24136         this.doc.designMode = 'on';
24137        
24138         this.doc.open();
24139         this.doc.write(this.getDocMarkup());
24140         this.doc.close();
24141
24142         
24143         var task = { // must defer to wait for browser to be ready
24144             run : function(){
24145                 //console.log("run task?" + this.doc.readyState);
24146                 this.assignDocWin();
24147                 if(this.doc.body || this.doc.readyState == 'complete'){
24148                     try {
24149                         this.doc.designMode="on";
24150                     } catch (e) {
24151                         return;
24152                     }
24153                     Roo.TaskMgr.stop(task);
24154                     this.initEditor.defer(10, this);
24155                 }
24156             },
24157             interval : 10,
24158             duration: 10000,
24159             scope: this
24160         };
24161         Roo.TaskMgr.start(task);
24162
24163     },
24164
24165     // private
24166     onResize : function(w, h)
24167     {
24168          Roo.log('resize: ' +w + ',' + h );
24169         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24170         if(!this.iframe){
24171             return;
24172         }
24173         if(typeof w == 'number'){
24174             
24175             this.iframe.style.width = w + 'px';
24176         }
24177         if(typeof h == 'number'){
24178             
24179             this.iframe.style.height = h + 'px';
24180             if(this.doc){
24181                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24182             }
24183         }
24184         
24185     },
24186
24187     /**
24188      * Toggles the editor between standard and source edit mode.
24189      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24190      */
24191     toggleSourceEdit : function(sourceEditMode){
24192         
24193         this.sourceEditMode = sourceEditMode === true;
24194         
24195         if(this.sourceEditMode){
24196  
24197             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24198             
24199         }else{
24200             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24201             //this.iframe.className = '';
24202             this.deferFocus();
24203         }
24204         //this.setSize(this.owner.wrap.getSize());
24205         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24206     },
24207
24208     
24209   
24210
24211     /**
24212      * Protected method that will not generally be called directly. If you need/want
24213      * custom HTML cleanup, this is the method you should override.
24214      * @param {String} html The HTML to be cleaned
24215      * return {String} The cleaned HTML
24216      */
24217     cleanHtml : function(html){
24218         html = String(html);
24219         if(html.length > 5){
24220             if(Roo.isSafari){ // strip safari nonsense
24221                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24222             }
24223         }
24224         if(html == '&nbsp;'){
24225             html = '';
24226         }
24227         return html;
24228     },
24229
24230     /**
24231      * HTML Editor -> Textarea
24232      * Protected method that will not generally be called directly. Syncs the contents
24233      * of the editor iframe with the textarea.
24234      */
24235     syncValue : function(){
24236         if(this.initialized){
24237             var bd = (this.doc.body || this.doc.documentElement);
24238             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24239             var html = bd.innerHTML;
24240             if(Roo.isSafari){
24241                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24242                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24243                 if(m && m[1]){
24244                     html = '<div style="'+m[0]+'">' + html + '</div>';
24245                 }
24246             }
24247             html = this.cleanHtml(html);
24248             // fix up the special chars.. normaly like back quotes in word...
24249             // however we do not want to do this with chinese..
24250             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24251                 
24252                 var cc = match.charCodeAt();
24253
24254                 // Get the character value, handling surrogate pairs
24255                 if (match.length == 2) {
24256                     // It's a surrogate pair, calculate the Unicode code point
24257                     var high = match.charCodeAt(0) - 0xD800;
24258                     var low  = match.charCodeAt(1) - 0xDC00;
24259                     cc = (high * 0x400) + low + 0x10000;
24260                 }  else if (
24261                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24262                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24263                     (cc >= 0xf900 && cc < 0xfb00 )
24264                 ) {
24265                         return match;
24266                 }  
24267          
24268                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24269                 return "&#" + cc + ";";
24270                 
24271                 
24272             });
24273             
24274             
24275              
24276             if(this.owner.fireEvent('beforesync', this, html) !== false){
24277                 this.el.dom.value = html;
24278                 this.owner.fireEvent('sync', this, html);
24279             }
24280         }
24281     },
24282
24283     /**
24284      * Protected method that will not generally be called directly. Pushes the value of the textarea
24285      * into the iframe editor.
24286      */
24287     pushValue : function(){
24288         if(this.initialized){
24289             var v = this.el.dom.value.trim();
24290             
24291 //            if(v.length < 1){
24292 //                v = '&#160;';
24293 //            }
24294             
24295             if(this.owner.fireEvent('beforepush', this, v) !== false){
24296                 var d = (this.doc.body || this.doc.documentElement);
24297                 d.innerHTML = v;
24298                 this.cleanUpPaste();
24299                 this.el.dom.value = d.innerHTML;
24300                 this.owner.fireEvent('push', this, v);
24301             }
24302         }
24303     },
24304
24305     // private
24306     deferFocus : function(){
24307         this.focus.defer(10, this);
24308     },
24309
24310     // doc'ed in Field
24311     focus : function(){
24312         if(this.win && !this.sourceEditMode){
24313             this.win.focus();
24314         }else{
24315             this.el.focus();
24316         }
24317     },
24318     
24319     assignDocWin: function()
24320     {
24321         var iframe = this.iframe;
24322         
24323          if(Roo.isIE){
24324             this.doc = iframe.contentWindow.document;
24325             this.win = iframe.contentWindow;
24326         } else {
24327 //            if (!Roo.get(this.frameId)) {
24328 //                return;
24329 //            }
24330 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24331 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24332             
24333             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24334                 return;
24335             }
24336             
24337             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24338             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24339         }
24340     },
24341     
24342     // private
24343     initEditor : function(){
24344         //console.log("INIT EDITOR");
24345         this.assignDocWin();
24346         
24347         
24348         
24349         this.doc.designMode="on";
24350         this.doc.open();
24351         this.doc.write(this.getDocMarkup());
24352         this.doc.close();
24353         
24354         var dbody = (this.doc.body || this.doc.documentElement);
24355         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24356         // this copies styles from the containing element into thsi one..
24357         // not sure why we need all of this..
24358         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24359         
24360         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24361         //ss['background-attachment'] = 'fixed'; // w3c
24362         dbody.bgProperties = 'fixed'; // ie
24363         //Roo.DomHelper.applyStyles(dbody, ss);
24364         Roo.EventManager.on(this.doc, {
24365             //'mousedown': this.onEditorEvent,
24366             'mouseup': this.onEditorEvent,
24367             'dblclick': this.onEditorEvent,
24368             'click': this.onEditorEvent,
24369             'keyup': this.onEditorEvent,
24370             buffer:100,
24371             scope: this
24372         });
24373         if(Roo.isGecko){
24374             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24375         }
24376         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24377             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24378         }
24379         this.initialized = true;
24380
24381         this.owner.fireEvent('initialize', this);
24382         this.pushValue();
24383     },
24384
24385     // private
24386     onDestroy : function(){
24387         
24388         
24389         
24390         if(this.rendered){
24391             
24392             //for (var i =0; i < this.toolbars.length;i++) {
24393             //    // fixme - ask toolbars for heights?
24394             //    this.toolbars[i].onDestroy();
24395            // }
24396             
24397             //this.wrap.dom.innerHTML = '';
24398             //this.wrap.remove();
24399         }
24400     },
24401
24402     // private
24403     onFirstFocus : function(){
24404         
24405         this.assignDocWin();
24406         
24407         
24408         this.activated = true;
24409          
24410     
24411         if(Roo.isGecko){ // prevent silly gecko errors
24412             this.win.focus();
24413             var s = this.win.getSelection();
24414             if(!s.focusNode || s.focusNode.nodeType != 3){
24415                 var r = s.getRangeAt(0);
24416                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24417                 r.collapse(true);
24418                 this.deferFocus();
24419             }
24420             try{
24421                 this.execCmd('useCSS', true);
24422                 this.execCmd('styleWithCSS', false);
24423             }catch(e){}
24424         }
24425         this.owner.fireEvent('activate', this);
24426     },
24427
24428     // private
24429     adjustFont: function(btn){
24430         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24431         //if(Roo.isSafari){ // safari
24432         //    adjust *= 2;
24433        // }
24434         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24435         if(Roo.isSafari){ // safari
24436             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24437             v =  (v < 10) ? 10 : v;
24438             v =  (v > 48) ? 48 : v;
24439             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24440             
24441         }
24442         
24443         
24444         v = Math.max(1, v+adjust);
24445         
24446         this.execCmd('FontSize', v  );
24447     },
24448
24449     onEditorEvent : function(e)
24450     {
24451         this.owner.fireEvent('editorevent', this, e);
24452       //  this.updateToolbar();
24453         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24454     },
24455
24456     insertTag : function(tg)
24457     {
24458         // could be a bit smarter... -> wrap the current selected tRoo..
24459         if (tg.toLowerCase() == 'span' ||
24460             tg.toLowerCase() == 'code' ||
24461             tg.toLowerCase() == 'sup' ||
24462             tg.toLowerCase() == 'sub' 
24463             ) {
24464             
24465             range = this.createRange(this.getSelection());
24466             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24467             wrappingNode.appendChild(range.extractContents());
24468             range.insertNode(wrappingNode);
24469
24470             return;
24471             
24472             
24473             
24474         }
24475         this.execCmd("formatblock",   tg);
24476         
24477     },
24478     
24479     insertText : function(txt)
24480     {
24481         
24482         
24483         var range = this.createRange();
24484         range.deleteContents();
24485                //alert(Sender.getAttribute('label'));
24486                
24487         range.insertNode(this.doc.createTextNode(txt));
24488     } ,
24489     
24490      
24491
24492     /**
24493      * Executes a Midas editor command on the editor document and performs necessary focus and
24494      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24495      * @param {String} cmd The Midas command
24496      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24497      */
24498     relayCmd : function(cmd, value){
24499         this.win.focus();
24500         this.execCmd(cmd, value);
24501         this.owner.fireEvent('editorevent', this);
24502         //this.updateToolbar();
24503         this.owner.deferFocus();
24504     },
24505
24506     /**
24507      * Executes a Midas editor command directly on the editor document.
24508      * For visual commands, you should use {@link #relayCmd} instead.
24509      * <b>This should only be called after the editor is initialized.</b>
24510      * @param {String} cmd The Midas command
24511      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24512      */
24513     execCmd : function(cmd, value){
24514         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24515         this.syncValue();
24516     },
24517  
24518  
24519    
24520     /**
24521      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24522      * to insert tRoo.
24523      * @param {String} text | dom node.. 
24524      */
24525     insertAtCursor : function(text)
24526     {
24527         
24528         if(!this.activated){
24529             return;
24530         }
24531         /*
24532         if(Roo.isIE){
24533             this.win.focus();
24534             var r = this.doc.selection.createRange();
24535             if(r){
24536                 r.collapse(true);
24537                 r.pasteHTML(text);
24538                 this.syncValue();
24539                 this.deferFocus();
24540             
24541             }
24542             return;
24543         }
24544         */
24545         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24546             this.win.focus();
24547             
24548             
24549             // from jquery ui (MIT licenced)
24550             var range, node;
24551             var win = this.win;
24552             
24553             if (win.getSelection && win.getSelection().getRangeAt) {
24554                 range = win.getSelection().getRangeAt(0);
24555                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24556                 range.insertNode(node);
24557             } else if (win.document.selection && win.document.selection.createRange) {
24558                 // no firefox support
24559                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24560                 win.document.selection.createRange().pasteHTML(txt);
24561             } else {
24562                 // no firefox support
24563                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24564                 this.execCmd('InsertHTML', txt);
24565             } 
24566             
24567             this.syncValue();
24568             
24569             this.deferFocus();
24570         }
24571     },
24572  // private
24573     mozKeyPress : function(e){
24574         if(e.ctrlKey){
24575             var c = e.getCharCode(), cmd;
24576           
24577             if(c > 0){
24578                 c = String.fromCharCode(c).toLowerCase();
24579                 switch(c){
24580                     case 'b':
24581                         cmd = 'bold';
24582                         break;
24583                     case 'i':
24584                         cmd = 'italic';
24585                         break;
24586                     
24587                     case 'u':
24588                         cmd = 'underline';
24589                         break;
24590                     
24591                     case 'v':
24592                         this.cleanUpPaste.defer(100, this);
24593                         return;
24594                         
24595                 }
24596                 if(cmd){
24597                     this.win.focus();
24598                     this.execCmd(cmd);
24599                     this.deferFocus();
24600                     e.preventDefault();
24601                 }
24602                 
24603             }
24604         }
24605     },
24606
24607     // private
24608     fixKeys : function(){ // load time branching for fastest keydown performance
24609         if(Roo.isIE){
24610             return function(e){
24611                 var k = e.getKey(), r;
24612                 if(k == e.TAB){
24613                     e.stopEvent();
24614                     r = this.doc.selection.createRange();
24615                     if(r){
24616                         r.collapse(true);
24617                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24618                         this.deferFocus();
24619                     }
24620                     return;
24621                 }
24622                 
24623                 if(k == e.ENTER){
24624                     r = this.doc.selection.createRange();
24625                     if(r){
24626                         var target = r.parentElement();
24627                         if(!target || target.tagName.toLowerCase() != 'li'){
24628                             e.stopEvent();
24629                             r.pasteHTML('<br />');
24630                             r.collapse(false);
24631                             r.select();
24632                         }
24633                     }
24634                 }
24635                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24636                     this.cleanUpPaste.defer(100, this);
24637                     return;
24638                 }
24639                 
24640                 
24641             };
24642         }else if(Roo.isOpera){
24643             return function(e){
24644                 var k = e.getKey();
24645                 if(k == e.TAB){
24646                     e.stopEvent();
24647                     this.win.focus();
24648                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24649                     this.deferFocus();
24650                 }
24651                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24652                     this.cleanUpPaste.defer(100, this);
24653                     return;
24654                 }
24655                 
24656             };
24657         }else if(Roo.isSafari){
24658             return function(e){
24659                 var k = e.getKey();
24660                 
24661                 if(k == e.TAB){
24662                     e.stopEvent();
24663                     this.execCmd('InsertText','\t');
24664                     this.deferFocus();
24665                     return;
24666                 }
24667                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24668                     this.cleanUpPaste.defer(100, this);
24669                     return;
24670                 }
24671                 
24672              };
24673         }
24674     }(),
24675     
24676     getAllAncestors: function()
24677     {
24678         var p = this.getSelectedNode();
24679         var a = [];
24680         if (!p) {
24681             a.push(p); // push blank onto stack..
24682             p = this.getParentElement();
24683         }
24684         
24685         
24686         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24687             a.push(p);
24688             p = p.parentNode;
24689         }
24690         a.push(this.doc.body);
24691         return a;
24692     },
24693     lastSel : false,
24694     lastSelNode : false,
24695     
24696     
24697     getSelection : function() 
24698     {
24699         this.assignDocWin();
24700         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24701     },
24702     
24703     getSelectedNode: function() 
24704     {
24705         // this may only work on Gecko!!!
24706         
24707         // should we cache this!!!!
24708         
24709         
24710         
24711          
24712         var range = this.createRange(this.getSelection()).cloneRange();
24713         
24714         if (Roo.isIE) {
24715             var parent = range.parentElement();
24716             while (true) {
24717                 var testRange = range.duplicate();
24718                 testRange.moveToElementText(parent);
24719                 if (testRange.inRange(range)) {
24720                     break;
24721                 }
24722                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24723                     break;
24724                 }
24725                 parent = parent.parentElement;
24726             }
24727             return parent;
24728         }
24729         
24730         // is ancestor a text element.
24731         var ac =  range.commonAncestorContainer;
24732         if (ac.nodeType == 3) {
24733             ac = ac.parentNode;
24734         }
24735         
24736         var ar = ac.childNodes;
24737          
24738         var nodes = [];
24739         var other_nodes = [];
24740         var has_other_nodes = false;
24741         for (var i=0;i<ar.length;i++) {
24742             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24743                 continue;
24744             }
24745             // fullly contained node.
24746             
24747             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24748                 nodes.push(ar[i]);
24749                 continue;
24750             }
24751             
24752             // probably selected..
24753             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24754                 other_nodes.push(ar[i]);
24755                 continue;
24756             }
24757             // outer..
24758             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24759                 continue;
24760             }
24761             
24762             
24763             has_other_nodes = true;
24764         }
24765         if (!nodes.length && other_nodes.length) {
24766             nodes= other_nodes;
24767         }
24768         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24769             return false;
24770         }
24771         
24772         return nodes[0];
24773     },
24774     createRange: function(sel)
24775     {
24776         // this has strange effects when using with 
24777         // top toolbar - not sure if it's a great idea.
24778         //this.editor.contentWindow.focus();
24779         if (typeof sel != "undefined") {
24780             try {
24781                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24782             } catch(e) {
24783                 return this.doc.createRange();
24784             }
24785         } else {
24786             return this.doc.createRange();
24787         }
24788     },
24789     getParentElement: function()
24790     {
24791         
24792         this.assignDocWin();
24793         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24794         
24795         var range = this.createRange(sel);
24796          
24797         try {
24798             var p = range.commonAncestorContainer;
24799             while (p.nodeType == 3) { // text node
24800                 p = p.parentNode;
24801             }
24802             return p;
24803         } catch (e) {
24804             return null;
24805         }
24806     
24807     },
24808     /***
24809      *
24810      * Range intersection.. the hard stuff...
24811      *  '-1' = before
24812      *  '0' = hits..
24813      *  '1' = after.
24814      *         [ -- selected range --- ]
24815      *   [fail]                        [fail]
24816      *
24817      *    basically..
24818      *      if end is before start or  hits it. fail.
24819      *      if start is after end or hits it fail.
24820      *
24821      *   if either hits (but other is outside. - then it's not 
24822      *   
24823      *    
24824      **/
24825     
24826     
24827     // @see http://www.thismuchiknow.co.uk/?p=64.
24828     rangeIntersectsNode : function(range, node)
24829     {
24830         var nodeRange = node.ownerDocument.createRange();
24831         try {
24832             nodeRange.selectNode(node);
24833         } catch (e) {
24834             nodeRange.selectNodeContents(node);
24835         }
24836     
24837         var rangeStartRange = range.cloneRange();
24838         rangeStartRange.collapse(true);
24839     
24840         var rangeEndRange = range.cloneRange();
24841         rangeEndRange.collapse(false);
24842     
24843         var nodeStartRange = nodeRange.cloneRange();
24844         nodeStartRange.collapse(true);
24845     
24846         var nodeEndRange = nodeRange.cloneRange();
24847         nodeEndRange.collapse(false);
24848     
24849         return rangeStartRange.compareBoundaryPoints(
24850                  Range.START_TO_START, nodeEndRange) == -1 &&
24851                rangeEndRange.compareBoundaryPoints(
24852                  Range.START_TO_START, nodeStartRange) == 1;
24853         
24854          
24855     },
24856     rangeCompareNode : function(range, node)
24857     {
24858         var nodeRange = node.ownerDocument.createRange();
24859         try {
24860             nodeRange.selectNode(node);
24861         } catch (e) {
24862             nodeRange.selectNodeContents(node);
24863         }
24864         
24865         
24866         range.collapse(true);
24867     
24868         nodeRange.collapse(true);
24869      
24870         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24871         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24872          
24873         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24874         
24875         var nodeIsBefore   =  ss == 1;
24876         var nodeIsAfter    = ee == -1;
24877         
24878         if (nodeIsBefore && nodeIsAfter) {
24879             return 0; // outer
24880         }
24881         if (!nodeIsBefore && nodeIsAfter) {
24882             return 1; //right trailed.
24883         }
24884         
24885         if (nodeIsBefore && !nodeIsAfter) {
24886             return 2;  // left trailed.
24887         }
24888         // fully contined.
24889         return 3;
24890     },
24891
24892     // private? - in a new class?
24893     cleanUpPaste :  function()
24894     {
24895         // cleans up the whole document..
24896         Roo.log('cleanuppaste');
24897         
24898         this.cleanUpChildren(this.doc.body);
24899         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24900         if (clean != this.doc.body.innerHTML) {
24901             this.doc.body.innerHTML = clean;
24902         }
24903         
24904     },
24905     
24906     cleanWordChars : function(input) {// change the chars to hex code
24907         var he = Roo.HtmlEditorCore;
24908         
24909         var output = input;
24910         Roo.each(he.swapCodes, function(sw) { 
24911             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24912             
24913             output = output.replace(swapper, sw[1]);
24914         });
24915         
24916         return output;
24917     },
24918     
24919     
24920     cleanUpChildren : function (n)
24921     {
24922         if (!n.childNodes.length) {
24923             return;
24924         }
24925         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24926            this.cleanUpChild(n.childNodes[i]);
24927         }
24928     },
24929     
24930     
24931         
24932     
24933     cleanUpChild : function (node)
24934     {
24935         var ed = this;
24936         //console.log(node);
24937         if (node.nodeName == "#text") {
24938             // clean up silly Windows -- stuff?
24939             return; 
24940         }
24941         if (node.nodeName == "#comment") {
24942             node.parentNode.removeChild(node);
24943             // clean up silly Windows -- stuff?
24944             return; 
24945         }
24946         var lcname = node.tagName.toLowerCase();
24947         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24948         // whitelist of tags..
24949         
24950         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24951             // remove node.
24952             node.parentNode.removeChild(node);
24953             return;
24954             
24955         }
24956         
24957         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24958         
24959         // spans with no attributes - just remove them..
24960         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24961             remove_keep_children = true;
24962         }
24963         
24964         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24965         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24966         
24967         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24968         //    remove_keep_children = true;
24969         //}
24970         
24971         if (remove_keep_children) {
24972             this.cleanUpChildren(node);
24973             // inserts everything just before this node...
24974             while (node.childNodes.length) {
24975                 var cn = node.childNodes[0];
24976                 node.removeChild(cn);
24977                 node.parentNode.insertBefore(cn, node);
24978             }
24979             node.parentNode.removeChild(node);
24980             return;
24981         }
24982         
24983         if (!node.attributes || !node.attributes.length) {
24984             
24985           
24986             
24987             
24988             this.cleanUpChildren(node);
24989             return;
24990         }
24991         
24992         function cleanAttr(n,v)
24993         {
24994             
24995             if (v.match(/^\./) || v.match(/^\//)) {
24996                 return;
24997             }
24998             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24999                 return;
25000             }
25001             if (v.match(/^#/)) {
25002                 return;
25003             }
25004             if (v.match(/^\{/)) { // allow template editing.
25005                 return;
25006             }
25007 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25008             node.removeAttribute(n);
25009             
25010         }
25011         
25012         var cwhite = this.cwhite;
25013         var cblack = this.cblack;
25014             
25015         function cleanStyle(n,v)
25016         {
25017             if (v.match(/expression/)) { //XSS?? should we even bother..
25018                 node.removeAttribute(n);
25019                 return;
25020             }
25021             
25022             var parts = v.split(/;/);
25023             var clean = [];
25024             
25025             Roo.each(parts, function(p) {
25026                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25027                 if (!p.length) {
25028                     return true;
25029                 }
25030                 var l = p.split(':').shift().replace(/\s+/g,'');
25031                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25032                 
25033                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25034 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25035                     //node.removeAttribute(n);
25036                     return true;
25037                 }
25038                 //Roo.log()
25039                 // only allow 'c whitelisted system attributes'
25040                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25041 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25042                     //node.removeAttribute(n);
25043                     return true;
25044                 }
25045                 
25046                 
25047                  
25048                 
25049                 clean.push(p);
25050                 return true;
25051             });
25052             if (clean.length) { 
25053                 node.setAttribute(n, clean.join(';'));
25054             } else {
25055                 node.removeAttribute(n);
25056             }
25057             
25058         }
25059         
25060         
25061         for (var i = node.attributes.length-1; i > -1 ; i--) {
25062             var a = node.attributes[i];
25063             //console.log(a);
25064             
25065             if (a.name.toLowerCase().substr(0,2)=='on')  {
25066                 node.removeAttribute(a.name);
25067                 continue;
25068             }
25069             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25070                 node.removeAttribute(a.name);
25071                 continue;
25072             }
25073             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25074                 cleanAttr(a.name,a.value); // fixme..
25075                 continue;
25076             }
25077             if (a.name == 'style') {
25078                 cleanStyle(a.name,a.value);
25079                 continue;
25080             }
25081             /// clean up MS crap..
25082             // tecnically this should be a list of valid class'es..
25083             
25084             
25085             if (a.name == 'class') {
25086                 if (a.value.match(/^Mso/)) {
25087                     node.removeAttribute('class');
25088                 }
25089                 
25090                 if (a.value.match(/^body$/)) {
25091                     node.removeAttribute('class');
25092                 }
25093                 continue;
25094             }
25095             
25096             // style cleanup!?
25097             // class cleanup?
25098             
25099         }
25100         
25101         
25102         this.cleanUpChildren(node);
25103         
25104         
25105     },
25106     
25107     /**
25108      * Clean up MS wordisms...
25109      */
25110     cleanWord : function(node)
25111     {
25112         if (!node) {
25113             this.cleanWord(this.doc.body);
25114             return;
25115         }
25116         
25117         if(
25118                 node.nodeName == 'SPAN' &&
25119                 !node.hasAttributes() &&
25120                 node.childNodes.length == 1 &&
25121                 node.firstChild.nodeName == "#text"  
25122         ) {
25123             var textNode = node.firstChild;
25124             node.removeChild(textNode);
25125             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25126                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25127             }
25128             node.parentNode.insertBefore(textNode, node);
25129             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25130                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25131             }
25132             node.parentNode.removeChild(node);
25133         }
25134         
25135         if (node.nodeName == "#text") {
25136             // clean up silly Windows -- stuff?
25137             return; 
25138         }
25139         if (node.nodeName == "#comment") {
25140             node.parentNode.removeChild(node);
25141             // clean up silly Windows -- stuff?
25142             return; 
25143         }
25144         
25145         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25146             node.parentNode.removeChild(node);
25147             return;
25148         }
25149         //Roo.log(node.tagName);
25150         // remove - but keep children..
25151         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25152             //Roo.log('-- removed');
25153             while (node.childNodes.length) {
25154                 var cn = node.childNodes[0];
25155                 node.removeChild(cn);
25156                 node.parentNode.insertBefore(cn, node);
25157                 // move node to parent - and clean it..
25158                 this.cleanWord(cn);
25159             }
25160             node.parentNode.removeChild(node);
25161             /// no need to iterate chidlren = it's got none..
25162             //this.iterateChildren(node, this.cleanWord);
25163             return;
25164         }
25165         // clean styles
25166         if (node.className.length) {
25167             
25168             var cn = node.className.split(/\W+/);
25169             var cna = [];
25170             Roo.each(cn, function(cls) {
25171                 if (cls.match(/Mso[a-zA-Z]+/)) {
25172                     return;
25173                 }
25174                 cna.push(cls);
25175             });
25176             node.className = cna.length ? cna.join(' ') : '';
25177             if (!cna.length) {
25178                 node.removeAttribute("class");
25179             }
25180         }
25181         
25182         if (node.hasAttribute("lang")) {
25183             node.removeAttribute("lang");
25184         }
25185         
25186         if (node.hasAttribute("style")) {
25187             
25188             var styles = node.getAttribute("style").split(";");
25189             var nstyle = [];
25190             Roo.each(styles, function(s) {
25191                 if (!s.match(/:/)) {
25192                     return;
25193                 }
25194                 var kv = s.split(":");
25195                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25196                     return;
25197                 }
25198                 // what ever is left... we allow.
25199                 nstyle.push(s);
25200             });
25201             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25202             if (!nstyle.length) {
25203                 node.removeAttribute('style');
25204             }
25205         }
25206         this.iterateChildren(node, this.cleanWord);
25207         
25208         
25209         
25210     },
25211     /**
25212      * iterateChildren of a Node, calling fn each time, using this as the scole..
25213      * @param {DomNode} node node to iterate children of.
25214      * @param {Function} fn method of this class to call on each item.
25215      */
25216     iterateChildren : function(node, fn)
25217     {
25218         if (!node.childNodes.length) {
25219                 return;
25220         }
25221         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25222            fn.call(this, node.childNodes[i])
25223         }
25224     },
25225     
25226     
25227     /**
25228      * cleanTableWidths.
25229      *
25230      * Quite often pasting from word etc.. results in tables with column and widths.
25231      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25232      *
25233      */
25234     cleanTableWidths : function(node)
25235     {
25236          
25237          
25238         if (!node) {
25239             this.cleanTableWidths(this.doc.body);
25240             return;
25241         }
25242         
25243         // ignore list...
25244         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25245             return; 
25246         }
25247         Roo.log(node.tagName);
25248         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25249             this.iterateChildren(node, this.cleanTableWidths);
25250             return;
25251         }
25252         if (node.hasAttribute('width')) {
25253             node.removeAttribute('width');
25254         }
25255         
25256          
25257         if (node.hasAttribute("style")) {
25258             // pretty basic...
25259             
25260             var styles = node.getAttribute("style").split(";");
25261             var nstyle = [];
25262             Roo.each(styles, function(s) {
25263                 if (!s.match(/:/)) {
25264                     return;
25265                 }
25266                 var kv = s.split(":");
25267                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25268                     return;
25269                 }
25270                 // what ever is left... we allow.
25271                 nstyle.push(s);
25272             });
25273             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25274             if (!nstyle.length) {
25275                 node.removeAttribute('style');
25276             }
25277         }
25278         
25279         this.iterateChildren(node, this.cleanTableWidths);
25280         
25281         
25282     },
25283     
25284     
25285     
25286     
25287     domToHTML : function(currentElement, depth, nopadtext) {
25288         
25289         depth = depth || 0;
25290         nopadtext = nopadtext || false;
25291     
25292         if (!currentElement) {
25293             return this.domToHTML(this.doc.body);
25294         }
25295         
25296         //Roo.log(currentElement);
25297         var j;
25298         var allText = false;
25299         var nodeName = currentElement.nodeName;
25300         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25301         
25302         if  (nodeName == '#text') {
25303             
25304             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25305         }
25306         
25307         
25308         var ret = '';
25309         if (nodeName != 'BODY') {
25310              
25311             var i = 0;
25312             // Prints the node tagName, such as <A>, <IMG>, etc
25313             if (tagName) {
25314                 var attr = [];
25315                 for(i = 0; i < currentElement.attributes.length;i++) {
25316                     // quoting?
25317                     var aname = currentElement.attributes.item(i).name;
25318                     if (!currentElement.attributes.item(i).value.length) {
25319                         continue;
25320                     }
25321                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25322                 }
25323                 
25324                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25325             } 
25326             else {
25327                 
25328                 // eack
25329             }
25330         } else {
25331             tagName = false;
25332         }
25333         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25334             return ret;
25335         }
25336         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25337             nopadtext = true;
25338         }
25339         
25340         
25341         // Traverse the tree
25342         i = 0;
25343         var currentElementChild = currentElement.childNodes.item(i);
25344         var allText = true;
25345         var innerHTML  = '';
25346         lastnode = '';
25347         while (currentElementChild) {
25348             // Formatting code (indent the tree so it looks nice on the screen)
25349             var nopad = nopadtext;
25350             if (lastnode == 'SPAN') {
25351                 nopad  = true;
25352             }
25353             // text
25354             if  (currentElementChild.nodeName == '#text') {
25355                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25356                 toadd = nopadtext ? toadd : toadd.trim();
25357                 if (!nopad && toadd.length > 80) {
25358                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25359                 }
25360                 innerHTML  += toadd;
25361                 
25362                 i++;
25363                 currentElementChild = currentElement.childNodes.item(i);
25364                 lastNode = '';
25365                 continue;
25366             }
25367             allText = false;
25368             
25369             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25370                 
25371             // Recursively traverse the tree structure of the child node
25372             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25373             lastnode = currentElementChild.nodeName;
25374             i++;
25375             currentElementChild=currentElement.childNodes.item(i);
25376         }
25377         
25378         ret += innerHTML;
25379         
25380         if (!allText) {
25381                 // The remaining code is mostly for formatting the tree
25382             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25383         }
25384         
25385         
25386         if (tagName) {
25387             ret+= "</"+tagName+">";
25388         }
25389         return ret;
25390         
25391     },
25392         
25393     applyBlacklists : function()
25394     {
25395         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25396         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25397         
25398         this.white = [];
25399         this.black = [];
25400         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25401             if (b.indexOf(tag) > -1) {
25402                 return;
25403             }
25404             this.white.push(tag);
25405             
25406         }, this);
25407         
25408         Roo.each(w, function(tag) {
25409             if (b.indexOf(tag) > -1) {
25410                 return;
25411             }
25412             if (this.white.indexOf(tag) > -1) {
25413                 return;
25414             }
25415             this.white.push(tag);
25416             
25417         }, this);
25418         
25419         
25420         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25421             if (w.indexOf(tag) > -1) {
25422                 return;
25423             }
25424             this.black.push(tag);
25425             
25426         }, this);
25427         
25428         Roo.each(b, function(tag) {
25429             if (w.indexOf(tag) > -1) {
25430                 return;
25431             }
25432             if (this.black.indexOf(tag) > -1) {
25433                 return;
25434             }
25435             this.black.push(tag);
25436             
25437         }, this);
25438         
25439         
25440         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25441         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25442         
25443         this.cwhite = [];
25444         this.cblack = [];
25445         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25446             if (b.indexOf(tag) > -1) {
25447                 return;
25448             }
25449             this.cwhite.push(tag);
25450             
25451         }, this);
25452         
25453         Roo.each(w, function(tag) {
25454             if (b.indexOf(tag) > -1) {
25455                 return;
25456             }
25457             if (this.cwhite.indexOf(tag) > -1) {
25458                 return;
25459             }
25460             this.cwhite.push(tag);
25461             
25462         }, this);
25463         
25464         
25465         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25466             if (w.indexOf(tag) > -1) {
25467                 return;
25468             }
25469             this.cblack.push(tag);
25470             
25471         }, this);
25472         
25473         Roo.each(b, function(tag) {
25474             if (w.indexOf(tag) > -1) {
25475                 return;
25476             }
25477             if (this.cblack.indexOf(tag) > -1) {
25478                 return;
25479             }
25480             this.cblack.push(tag);
25481             
25482         }, this);
25483     },
25484     
25485     setStylesheets : function(stylesheets)
25486     {
25487         if(typeof(stylesheets) == 'string'){
25488             Roo.get(this.iframe.contentDocument.head).createChild({
25489                 tag : 'link',
25490                 rel : 'stylesheet',
25491                 type : 'text/css',
25492                 href : stylesheets
25493             });
25494             
25495             return;
25496         }
25497         var _this = this;
25498      
25499         Roo.each(stylesheets, function(s) {
25500             if(!s.length){
25501                 return;
25502             }
25503             
25504             Roo.get(_this.iframe.contentDocument.head).createChild({
25505                 tag : 'link',
25506                 rel : 'stylesheet',
25507                 type : 'text/css',
25508                 href : s
25509             });
25510         });
25511
25512         
25513     },
25514     
25515     removeStylesheets : function()
25516     {
25517         var _this = this;
25518         
25519         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25520             s.remove();
25521         });
25522     },
25523     
25524     setStyle : function(style)
25525     {
25526         Roo.get(this.iframe.contentDocument.head).createChild({
25527             tag : 'style',
25528             type : 'text/css',
25529             html : style
25530         });
25531
25532         return;
25533     }
25534     
25535     // hide stuff that is not compatible
25536     /**
25537      * @event blur
25538      * @hide
25539      */
25540     /**
25541      * @event change
25542      * @hide
25543      */
25544     /**
25545      * @event focus
25546      * @hide
25547      */
25548     /**
25549      * @event specialkey
25550      * @hide
25551      */
25552     /**
25553      * @cfg {String} fieldClass @hide
25554      */
25555     /**
25556      * @cfg {String} focusClass @hide
25557      */
25558     /**
25559      * @cfg {String} autoCreate @hide
25560      */
25561     /**
25562      * @cfg {String} inputType @hide
25563      */
25564     /**
25565      * @cfg {String} invalidClass @hide
25566      */
25567     /**
25568      * @cfg {String} invalidText @hide
25569      */
25570     /**
25571      * @cfg {String} msgFx @hide
25572      */
25573     /**
25574      * @cfg {String} validateOnBlur @hide
25575      */
25576 });
25577
25578 Roo.HtmlEditorCore.white = [
25579         'area', 'br', 'img', 'input', 'hr', 'wbr',
25580         
25581        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25582        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25583        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25584        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25585        'table',   'ul',         'xmp', 
25586        
25587        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25588       'thead',   'tr', 
25589      
25590       'dir', 'menu', 'ol', 'ul', 'dl',
25591        
25592       'embed',  'object'
25593 ];
25594
25595
25596 Roo.HtmlEditorCore.black = [
25597     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25598         'applet', // 
25599         'base',   'basefont', 'bgsound', 'blink',  'body', 
25600         'frame',  'frameset', 'head',    'html',   'ilayer', 
25601         'iframe', 'layer',  'link',     'meta',    'object',   
25602         'script', 'style' ,'title',  'xml' // clean later..
25603 ];
25604 Roo.HtmlEditorCore.clean = [
25605     'script', 'style', 'title', 'xml'
25606 ];
25607 Roo.HtmlEditorCore.remove = [
25608     'font'
25609 ];
25610 // attributes..
25611
25612 Roo.HtmlEditorCore.ablack = [
25613     'on'
25614 ];
25615     
25616 Roo.HtmlEditorCore.aclean = [ 
25617     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25618 ];
25619
25620 // protocols..
25621 Roo.HtmlEditorCore.pwhite= [
25622         'http',  'https',  'mailto'
25623 ];
25624
25625 // white listed style attributes.
25626 Roo.HtmlEditorCore.cwhite= [
25627       //  'text-align', /// default is to allow most things..
25628       
25629          
25630 //        'font-size'//??
25631 ];
25632
25633 // black listed style attributes.
25634 Roo.HtmlEditorCore.cblack= [
25635       //  'font-size' -- this can be set by the project 
25636 ];
25637
25638
25639 Roo.HtmlEditorCore.swapCodes   =[ 
25640     [    8211, "--" ], 
25641     [    8212, "--" ], 
25642     [    8216,  "'" ],  
25643     [    8217, "'" ],  
25644     [    8220, '"' ],  
25645     [    8221, '"' ],  
25646     [    8226, "*" ],  
25647     [    8230, "..." ]
25648 ]; 
25649
25650     /*
25651  * - LGPL
25652  *
25653  * HtmlEditor
25654  * 
25655  */
25656
25657 /**
25658  * @class Roo.bootstrap.HtmlEditor
25659  * @extends Roo.bootstrap.TextArea
25660  * Bootstrap HtmlEditor class
25661
25662  * @constructor
25663  * Create a new HtmlEditor
25664  * @param {Object} config The config object
25665  */
25666
25667 Roo.bootstrap.HtmlEditor = function(config){
25668     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25669     if (!this.toolbars) {
25670         this.toolbars = [];
25671     }
25672     
25673     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25674     this.addEvents({
25675             /**
25676              * @event initialize
25677              * Fires when the editor is fully initialized (including the iframe)
25678              * @param {HtmlEditor} this
25679              */
25680             initialize: true,
25681             /**
25682              * @event activate
25683              * Fires when the editor is first receives the focus. Any insertion must wait
25684              * until after this event.
25685              * @param {HtmlEditor} this
25686              */
25687             activate: true,
25688              /**
25689              * @event beforesync
25690              * Fires before the textarea is updated with content from the editor iframe. Return false
25691              * to cancel the sync.
25692              * @param {HtmlEditor} this
25693              * @param {String} html
25694              */
25695             beforesync: true,
25696              /**
25697              * @event beforepush
25698              * Fires before the iframe editor is updated with content from the textarea. Return false
25699              * to cancel the push.
25700              * @param {HtmlEditor} this
25701              * @param {String} html
25702              */
25703             beforepush: true,
25704              /**
25705              * @event sync
25706              * Fires when the textarea is updated with content from the editor iframe.
25707              * @param {HtmlEditor} this
25708              * @param {String} html
25709              */
25710             sync: true,
25711              /**
25712              * @event push
25713              * Fires when the iframe editor is updated with content from the textarea.
25714              * @param {HtmlEditor} this
25715              * @param {String} html
25716              */
25717             push: true,
25718              /**
25719              * @event editmodechange
25720              * Fires when the editor switches edit modes
25721              * @param {HtmlEditor} this
25722              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25723              */
25724             editmodechange: true,
25725             /**
25726              * @event editorevent
25727              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25728              * @param {HtmlEditor} this
25729              */
25730             editorevent: true,
25731             /**
25732              * @event firstfocus
25733              * Fires when on first focus - needed by toolbars..
25734              * @param {HtmlEditor} this
25735              */
25736             firstfocus: true,
25737             /**
25738              * @event autosave
25739              * Auto save the htmlEditor value as a file into Events
25740              * @param {HtmlEditor} this
25741              */
25742             autosave: true,
25743             /**
25744              * @event savedpreview
25745              * preview the saved version of htmlEditor
25746              * @param {HtmlEditor} this
25747              */
25748             savedpreview: true
25749         });
25750 };
25751
25752
25753 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25754     
25755     
25756       /**
25757      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25758      */
25759     toolbars : false,
25760     
25761      /**
25762     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25763     */
25764     btns : [],
25765    
25766      /**
25767      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25768      *                        Roo.resizable.
25769      */
25770     resizable : false,
25771      /**
25772      * @cfg {Number} height (in pixels)
25773      */   
25774     height: 300,
25775    /**
25776      * @cfg {Number} width (in pixels)
25777      */   
25778     width: false,
25779     
25780     /**
25781      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25782      * 
25783      */
25784     stylesheets: false,
25785     
25786     // id of frame..
25787     frameId: false,
25788     
25789     // private properties
25790     validationEvent : false,
25791     deferHeight: true,
25792     initialized : false,
25793     activated : false,
25794     
25795     onFocus : Roo.emptyFn,
25796     iframePad:3,
25797     hideMode:'offsets',
25798     
25799     tbContainer : false,
25800     
25801     bodyCls : '',
25802     
25803     toolbarContainer :function() {
25804         return this.wrap.select('.x-html-editor-tb',true).first();
25805     },
25806
25807     /**
25808      * Protected method that will not generally be called directly. It
25809      * is called when the editor creates its toolbar. Override this method if you need to
25810      * add custom toolbar buttons.
25811      * @param {HtmlEditor} editor
25812      */
25813     createToolbar : function(){
25814         Roo.log('renewing');
25815         Roo.log("create toolbars");
25816         
25817         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25818         this.toolbars[0].render(this.toolbarContainer());
25819         
25820         return;
25821         
25822 //        if (!editor.toolbars || !editor.toolbars.length) {
25823 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25824 //        }
25825 //        
25826 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25827 //            editor.toolbars[i] = Roo.factory(
25828 //                    typeof(editor.toolbars[i]) == 'string' ?
25829 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25830 //                Roo.bootstrap.HtmlEditor);
25831 //            editor.toolbars[i].init(editor);
25832 //        }
25833     },
25834
25835      
25836     // private
25837     onRender : function(ct, position)
25838     {
25839        // Roo.log("Call onRender: " + this.xtype);
25840         var _t = this;
25841         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25842       
25843         this.wrap = this.inputEl().wrap({
25844             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25845         });
25846         
25847         this.editorcore.onRender(ct, position);
25848          
25849         if (this.resizable) {
25850             this.resizeEl = new Roo.Resizable(this.wrap, {
25851                 pinned : true,
25852                 wrap: true,
25853                 dynamic : true,
25854                 minHeight : this.height,
25855                 height: this.height,
25856                 handles : this.resizable,
25857                 width: this.width,
25858                 listeners : {
25859                     resize : function(r, w, h) {
25860                         _t.onResize(w,h); // -something
25861                     }
25862                 }
25863             });
25864             
25865         }
25866         this.createToolbar(this);
25867        
25868         
25869         if(!this.width && this.resizable){
25870             this.setSize(this.wrap.getSize());
25871         }
25872         if (this.resizeEl) {
25873             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25874             // should trigger onReize..
25875         }
25876         
25877     },
25878
25879     // private
25880     onResize : function(w, h)
25881     {
25882         Roo.log('resize: ' +w + ',' + h );
25883         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25884         var ew = false;
25885         var eh = false;
25886         
25887         if(this.inputEl() ){
25888             if(typeof w == 'number'){
25889                 var aw = w - this.wrap.getFrameWidth('lr');
25890                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25891                 ew = aw;
25892             }
25893             if(typeof h == 'number'){
25894                  var tbh = -11;  // fixme it needs to tool bar size!
25895                 for (var i =0; i < this.toolbars.length;i++) {
25896                     // fixme - ask toolbars for heights?
25897                     tbh += this.toolbars[i].el.getHeight();
25898                     //if (this.toolbars[i].footer) {
25899                     //    tbh += this.toolbars[i].footer.el.getHeight();
25900                     //}
25901                 }
25902               
25903                 
25904                 
25905                 
25906                 
25907                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25908                 ah -= 5; // knock a few pixes off for look..
25909                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25910                 var eh = ah;
25911             }
25912         }
25913         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25914         this.editorcore.onResize(ew,eh);
25915         
25916     },
25917
25918     /**
25919      * Toggles the editor between standard and source edit mode.
25920      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25921      */
25922     toggleSourceEdit : function(sourceEditMode)
25923     {
25924         this.editorcore.toggleSourceEdit(sourceEditMode);
25925         
25926         if(this.editorcore.sourceEditMode){
25927             Roo.log('editor - showing textarea');
25928             
25929 //            Roo.log('in');
25930 //            Roo.log(this.syncValue());
25931             this.syncValue();
25932             this.inputEl().removeClass(['hide', 'x-hidden']);
25933             this.inputEl().dom.removeAttribute('tabIndex');
25934             this.inputEl().focus();
25935         }else{
25936             Roo.log('editor - hiding textarea');
25937 //            Roo.log('out')
25938 //            Roo.log(this.pushValue()); 
25939             this.pushValue();
25940             
25941             this.inputEl().addClass(['hide', 'x-hidden']);
25942             this.inputEl().dom.setAttribute('tabIndex', -1);
25943             //this.deferFocus();
25944         }
25945          
25946         if(this.resizable){
25947             this.setSize(this.wrap.getSize());
25948         }
25949         
25950         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25951     },
25952  
25953     // private (for BoxComponent)
25954     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25955
25956     // private (for BoxComponent)
25957     getResizeEl : function(){
25958         return this.wrap;
25959     },
25960
25961     // private (for BoxComponent)
25962     getPositionEl : function(){
25963         return this.wrap;
25964     },
25965
25966     // private
25967     initEvents : function(){
25968         this.originalValue = this.getValue();
25969     },
25970
25971 //    /**
25972 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25973 //     * @method
25974 //     */
25975 //    markInvalid : Roo.emptyFn,
25976 //    /**
25977 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25978 //     * @method
25979 //     */
25980 //    clearInvalid : Roo.emptyFn,
25981
25982     setValue : function(v){
25983         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25984         this.editorcore.pushValue();
25985     },
25986
25987      
25988     // private
25989     deferFocus : function(){
25990         this.focus.defer(10, this);
25991     },
25992
25993     // doc'ed in Field
25994     focus : function(){
25995         this.editorcore.focus();
25996         
25997     },
25998       
25999
26000     // private
26001     onDestroy : function(){
26002         
26003         
26004         
26005         if(this.rendered){
26006             
26007             for (var i =0; i < this.toolbars.length;i++) {
26008                 // fixme - ask toolbars for heights?
26009                 this.toolbars[i].onDestroy();
26010             }
26011             
26012             this.wrap.dom.innerHTML = '';
26013             this.wrap.remove();
26014         }
26015     },
26016
26017     // private
26018     onFirstFocus : function(){
26019         //Roo.log("onFirstFocus");
26020         this.editorcore.onFirstFocus();
26021          for (var i =0; i < this.toolbars.length;i++) {
26022             this.toolbars[i].onFirstFocus();
26023         }
26024         
26025     },
26026     
26027     // private
26028     syncValue : function()
26029     {   
26030         this.editorcore.syncValue();
26031     },
26032     
26033     pushValue : function()
26034     {   
26035         this.editorcore.pushValue();
26036     }
26037      
26038     
26039     // hide stuff that is not compatible
26040     /**
26041      * @event blur
26042      * @hide
26043      */
26044     /**
26045      * @event change
26046      * @hide
26047      */
26048     /**
26049      * @event focus
26050      * @hide
26051      */
26052     /**
26053      * @event specialkey
26054      * @hide
26055      */
26056     /**
26057      * @cfg {String} fieldClass @hide
26058      */
26059     /**
26060      * @cfg {String} focusClass @hide
26061      */
26062     /**
26063      * @cfg {String} autoCreate @hide
26064      */
26065     /**
26066      * @cfg {String} inputType @hide
26067      */
26068      
26069     /**
26070      * @cfg {String} invalidText @hide
26071      */
26072     /**
26073      * @cfg {String} msgFx @hide
26074      */
26075     /**
26076      * @cfg {String} validateOnBlur @hide
26077      */
26078 });
26079  
26080     
26081    
26082    
26083    
26084       
26085 Roo.namespace('Roo.bootstrap.htmleditor');
26086 /**
26087  * @class Roo.bootstrap.HtmlEditorToolbar1
26088  * Basic Toolbar
26089  * 
26090  * @example
26091  * Usage:
26092  *
26093  new Roo.bootstrap.HtmlEditor({
26094     ....
26095     toolbars : [
26096         new Roo.bootstrap.HtmlEditorToolbar1({
26097             disable : { fonts: 1 , format: 1, ..., ... , ...],
26098             btns : [ .... ]
26099         })
26100     }
26101      
26102  * 
26103  * @cfg {Object} disable List of elements to disable..
26104  * @cfg {Array} btns List of additional buttons.
26105  * 
26106  * 
26107  * NEEDS Extra CSS? 
26108  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26109  */
26110  
26111 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26112 {
26113     
26114     Roo.apply(this, config);
26115     
26116     // default disabled, based on 'good practice'..
26117     this.disable = this.disable || {};
26118     Roo.applyIf(this.disable, {
26119         fontSize : true,
26120         colors : true,
26121         specialElements : true
26122     });
26123     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26124     
26125     this.editor = config.editor;
26126     this.editorcore = config.editor.editorcore;
26127     
26128     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26129     
26130     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26131     // dont call parent... till later.
26132 }
26133 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26134      
26135     bar : true,
26136     
26137     editor : false,
26138     editorcore : false,
26139     
26140     
26141     formats : [
26142         "p" ,  
26143         "h1","h2","h3","h4","h5","h6", 
26144         "pre", "code", 
26145         "abbr", "acronym", "address", "cite", "samp", "var",
26146         'div','span'
26147     ],
26148     
26149     onRender : function(ct, position)
26150     {
26151        // Roo.log("Call onRender: " + this.xtype);
26152         
26153        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26154        Roo.log(this.el);
26155        this.el.dom.style.marginBottom = '0';
26156        var _this = this;
26157        var editorcore = this.editorcore;
26158        var editor= this.editor;
26159        
26160        var children = [];
26161        var btn = function(id,cmd , toggle, handler, html){
26162        
26163             var  event = toggle ? 'toggle' : 'click';
26164        
26165             var a = {
26166                 size : 'sm',
26167                 xtype: 'Button',
26168                 xns: Roo.bootstrap,
26169                 //glyphicon : id,
26170                 fa: id,
26171                 cmd : id || cmd,
26172                 enableToggle:toggle !== false,
26173                 html : html || '',
26174                 pressed : toggle ? false : null,
26175                 listeners : {}
26176             };
26177             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26178                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26179             };
26180             children.push(a);
26181             return a;
26182        }
26183        
26184     //    var cb_box = function...
26185         
26186         var style = {
26187                 xtype: 'Button',
26188                 size : 'sm',
26189                 xns: Roo.bootstrap,
26190                 fa : 'font',
26191                 //html : 'submit'
26192                 menu : {
26193                     xtype: 'Menu',
26194                     xns: Roo.bootstrap,
26195                     items:  []
26196                 }
26197         };
26198         Roo.each(this.formats, function(f) {
26199             style.menu.items.push({
26200                 xtype :'MenuItem',
26201                 xns: Roo.bootstrap,
26202                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26203                 tagname : f,
26204                 listeners : {
26205                     click : function()
26206                     {
26207                         editorcore.insertTag(this.tagname);
26208                         editor.focus();
26209                     }
26210                 }
26211                 
26212             });
26213         });
26214         children.push(style);   
26215         
26216         btn('bold',false,true);
26217         btn('italic',false,true);
26218         btn('align-left', 'justifyleft',true);
26219         btn('align-center', 'justifycenter',true);
26220         btn('align-right' , 'justifyright',true);
26221         btn('link', false, false, function(btn) {
26222             //Roo.log("create link?");
26223             var url = prompt(this.createLinkText, this.defaultLinkValue);
26224             if(url && url != 'http:/'+'/'){
26225                 this.editorcore.relayCmd('createlink', url);
26226             }
26227         }),
26228         btn('list','insertunorderedlist',true);
26229         btn('pencil', false,true, function(btn){
26230                 Roo.log(this);
26231                 this.toggleSourceEdit(btn.pressed);
26232         });
26233         
26234         if (this.editor.btns.length > 0) {
26235             for (var i = 0; i<this.editor.btns.length; i++) {
26236                 children.push(this.editor.btns[i]);
26237             }
26238         }
26239         
26240         /*
26241         var cog = {
26242                 xtype: 'Button',
26243                 size : 'sm',
26244                 xns: Roo.bootstrap,
26245                 glyphicon : 'cog',
26246                 //html : 'submit'
26247                 menu : {
26248                     xtype: 'Menu',
26249                     xns: Roo.bootstrap,
26250                     items:  []
26251                 }
26252         };
26253         
26254         cog.menu.items.push({
26255             xtype :'MenuItem',
26256             xns: Roo.bootstrap,
26257             html : Clean styles,
26258             tagname : f,
26259             listeners : {
26260                 click : function()
26261                 {
26262                     editorcore.insertTag(this.tagname);
26263                     editor.focus();
26264                 }
26265             }
26266             
26267         });
26268        */
26269         
26270          
26271        this.xtype = 'NavSimplebar';
26272         
26273         for(var i=0;i< children.length;i++) {
26274             
26275             this.buttons.add(this.addxtypeChild(children[i]));
26276             
26277         }
26278         
26279         editor.on('editorevent', this.updateToolbar, this);
26280     },
26281     onBtnClick : function(id)
26282     {
26283        this.editorcore.relayCmd(id);
26284        this.editorcore.focus();
26285     },
26286     
26287     /**
26288      * Protected method that will not generally be called directly. It triggers
26289      * a toolbar update by reading the markup state of the current selection in the editor.
26290      */
26291     updateToolbar: function(){
26292
26293         if(!this.editorcore.activated){
26294             this.editor.onFirstFocus(); // is this neeed?
26295             return;
26296         }
26297
26298         var btns = this.buttons; 
26299         var doc = this.editorcore.doc;
26300         btns.get('bold').setActive(doc.queryCommandState('bold'));
26301         btns.get('italic').setActive(doc.queryCommandState('italic'));
26302         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26303         
26304         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26305         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26306         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26307         
26308         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26309         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26310          /*
26311         
26312         var ans = this.editorcore.getAllAncestors();
26313         if (this.formatCombo) {
26314             
26315             
26316             var store = this.formatCombo.store;
26317             this.formatCombo.setValue("");
26318             for (var i =0; i < ans.length;i++) {
26319                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26320                     // select it..
26321                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26322                     break;
26323                 }
26324             }
26325         }
26326         
26327         
26328         
26329         // hides menus... - so this cant be on a menu...
26330         Roo.bootstrap.MenuMgr.hideAll();
26331         */
26332         Roo.bootstrap.MenuMgr.hideAll();
26333         //this.editorsyncValue();
26334     },
26335     onFirstFocus: function() {
26336         this.buttons.each(function(item){
26337            item.enable();
26338         });
26339     },
26340     toggleSourceEdit : function(sourceEditMode){
26341         
26342           
26343         if(sourceEditMode){
26344             Roo.log("disabling buttons");
26345            this.buttons.each( function(item){
26346                 if(item.cmd != 'pencil'){
26347                     item.disable();
26348                 }
26349             });
26350           
26351         }else{
26352             Roo.log("enabling buttons");
26353             if(this.editorcore.initialized){
26354                 this.buttons.each( function(item){
26355                     item.enable();
26356                 });
26357             }
26358             
26359         }
26360         Roo.log("calling toggole on editor");
26361         // tell the editor that it's been pressed..
26362         this.editor.toggleSourceEdit(sourceEditMode);
26363        
26364     }
26365 });
26366
26367
26368
26369
26370  
26371 /*
26372  * - LGPL
26373  */
26374
26375 /**
26376  * @class Roo.bootstrap.Markdown
26377  * @extends Roo.bootstrap.TextArea
26378  * Bootstrap Showdown editable area
26379  * @cfg {string} content
26380  * 
26381  * @constructor
26382  * Create a new Showdown
26383  */
26384
26385 Roo.bootstrap.Markdown = function(config){
26386     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26387    
26388 };
26389
26390 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26391     
26392     editing :false,
26393     
26394     initEvents : function()
26395     {
26396         
26397         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26398         this.markdownEl = this.el.createChild({
26399             cls : 'roo-markdown-area'
26400         });
26401         this.inputEl().addClass('d-none');
26402         if (this.getValue() == '') {
26403             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26404             
26405         } else {
26406             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26407         }
26408         this.markdownEl.on('click', this.toggleTextEdit, this);
26409         this.on('blur', this.toggleTextEdit, this);
26410         this.on('specialkey', this.resizeTextArea, this);
26411     },
26412     
26413     toggleTextEdit : function()
26414     {
26415         var sh = this.markdownEl.getHeight();
26416         this.inputEl().addClass('d-none');
26417         this.markdownEl.addClass('d-none');
26418         if (!this.editing) {
26419             // show editor?
26420             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26421             this.inputEl().removeClass('d-none');
26422             this.inputEl().focus();
26423             this.editing = true;
26424             return;
26425         }
26426         // show showdown...
26427         this.updateMarkdown();
26428         this.markdownEl.removeClass('d-none');
26429         this.editing = false;
26430         return;
26431     },
26432     updateMarkdown : function()
26433     {
26434         if (this.getValue() == '') {
26435             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26436             return;
26437         }
26438  
26439         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26440     },
26441     
26442     resizeTextArea: function () {
26443         
26444         var sh = 100;
26445         Roo.log([sh, this.getValue().split("\n").length * 30]);
26446         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26447     },
26448     setValue : function(val)
26449     {
26450         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26451         if (!this.editing) {
26452             this.updateMarkdown();
26453         }
26454         
26455     },
26456     focus : function()
26457     {
26458         if (!this.editing) {
26459             this.toggleTextEdit();
26460         }
26461         
26462     }
26463
26464
26465 });
26466 /**
26467  * @class Roo.bootstrap.Table.AbstractSelectionModel
26468  * @extends Roo.util.Observable
26469  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26470  * implemented by descendant classes.  This class should not be directly instantiated.
26471  * @constructor
26472  */
26473 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26474     this.locked = false;
26475     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26476 };
26477
26478
26479 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26480     /** @ignore Called by the grid automatically. Do not call directly. */
26481     init : function(grid){
26482         this.grid = grid;
26483         this.initEvents();
26484     },
26485
26486     /**
26487      * Locks the selections.
26488      */
26489     lock : function(){
26490         this.locked = true;
26491     },
26492
26493     /**
26494      * Unlocks the selections.
26495      */
26496     unlock : function(){
26497         this.locked = false;
26498     },
26499
26500     /**
26501      * Returns true if the selections are locked.
26502      * @return {Boolean}
26503      */
26504     isLocked : function(){
26505         return this.locked;
26506     },
26507     
26508     
26509     initEvents : function ()
26510     {
26511         
26512     }
26513 });
26514 /**
26515  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26516  * @class Roo.bootstrap.Table.RowSelectionModel
26517  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26518  * It supports multiple selections and keyboard selection/navigation. 
26519  * @constructor
26520  * @param {Object} config
26521  */
26522
26523 Roo.bootstrap.Table.RowSelectionModel = function(config){
26524     Roo.apply(this, config);
26525     this.selections = new Roo.util.MixedCollection(false, function(o){
26526         return o.id;
26527     });
26528
26529     this.last = false;
26530     this.lastActive = false;
26531
26532     this.addEvents({
26533         /**
26534              * @event selectionchange
26535              * Fires when the selection changes
26536              * @param {SelectionModel} this
26537              */
26538             "selectionchange" : true,
26539         /**
26540              * @event afterselectionchange
26541              * Fires after the selection changes (eg. by key press or clicking)
26542              * @param {SelectionModel} this
26543              */
26544             "afterselectionchange" : true,
26545         /**
26546              * @event beforerowselect
26547              * Fires when a row is selected being selected, return false to cancel.
26548              * @param {SelectionModel} this
26549              * @param {Number} rowIndex The selected index
26550              * @param {Boolean} keepExisting False if other selections will be cleared
26551              */
26552             "beforerowselect" : true,
26553         /**
26554              * @event rowselect
26555              * Fires when a row is selected.
26556              * @param {SelectionModel} this
26557              * @param {Number} rowIndex The selected index
26558              * @param {Roo.data.Record} r The record
26559              */
26560             "rowselect" : true,
26561         /**
26562              * @event rowdeselect
26563              * Fires when a row is deselected.
26564              * @param {SelectionModel} this
26565              * @param {Number} rowIndex The selected index
26566              */
26567         "rowdeselect" : true
26568     });
26569     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26570     this.locked = false;
26571  };
26572
26573 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26574     /**
26575      * @cfg {Boolean} singleSelect
26576      * True to allow selection of only one row at a time (defaults to false)
26577      */
26578     singleSelect : false,
26579
26580     // private
26581     initEvents : function()
26582     {
26583
26584         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26585         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26586         //}else{ // allow click to work like normal
26587          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26588         //}
26589         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26590         this.grid.on("rowclick", this.handleMouseDown, this);
26591         
26592         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26593             "up" : function(e){
26594                 if(!e.shiftKey){
26595                     this.selectPrevious(e.shiftKey);
26596                 }else if(this.last !== false && this.lastActive !== false){
26597                     var last = this.last;
26598                     this.selectRange(this.last,  this.lastActive-1);
26599                     this.grid.getView().focusRow(this.lastActive);
26600                     if(last !== false){
26601                         this.last = last;
26602                     }
26603                 }else{
26604                     this.selectFirstRow();
26605                 }
26606                 this.fireEvent("afterselectionchange", this);
26607             },
26608             "down" : function(e){
26609                 if(!e.shiftKey){
26610                     this.selectNext(e.shiftKey);
26611                 }else if(this.last !== false && this.lastActive !== false){
26612                     var last = this.last;
26613                     this.selectRange(this.last,  this.lastActive+1);
26614                     this.grid.getView().focusRow(this.lastActive);
26615                     if(last !== false){
26616                         this.last = last;
26617                     }
26618                 }else{
26619                     this.selectFirstRow();
26620                 }
26621                 this.fireEvent("afterselectionchange", this);
26622             },
26623             scope: this
26624         });
26625         this.grid.store.on('load', function(){
26626             this.selections.clear();
26627         },this);
26628         /*
26629         var view = this.grid.view;
26630         view.on("refresh", this.onRefresh, this);
26631         view.on("rowupdated", this.onRowUpdated, this);
26632         view.on("rowremoved", this.onRemove, this);
26633         */
26634     },
26635
26636     // private
26637     onRefresh : function()
26638     {
26639         var ds = this.grid.store, i, v = this.grid.view;
26640         var s = this.selections;
26641         s.each(function(r){
26642             if((i = ds.indexOfId(r.id)) != -1){
26643                 v.onRowSelect(i);
26644             }else{
26645                 s.remove(r);
26646             }
26647         });
26648     },
26649
26650     // private
26651     onRemove : function(v, index, r){
26652         this.selections.remove(r);
26653     },
26654
26655     // private
26656     onRowUpdated : function(v, index, r){
26657         if(this.isSelected(r)){
26658             v.onRowSelect(index);
26659         }
26660     },
26661
26662     /**
26663      * Select records.
26664      * @param {Array} records The records to select
26665      * @param {Boolean} keepExisting (optional) True to keep existing selections
26666      */
26667     selectRecords : function(records, keepExisting)
26668     {
26669         if(!keepExisting){
26670             this.clearSelections();
26671         }
26672             var ds = this.grid.store;
26673         for(var i = 0, len = records.length; i < len; i++){
26674             this.selectRow(ds.indexOf(records[i]), true);
26675         }
26676     },
26677
26678     /**
26679      * Gets the number of selected rows.
26680      * @return {Number}
26681      */
26682     getCount : function(){
26683         return this.selections.length;
26684     },
26685
26686     /**
26687      * Selects the first row in the grid.
26688      */
26689     selectFirstRow : function(){
26690         this.selectRow(0);
26691     },
26692
26693     /**
26694      * Select the last row.
26695      * @param {Boolean} keepExisting (optional) True to keep existing selections
26696      */
26697     selectLastRow : function(keepExisting){
26698         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26699         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26700     },
26701
26702     /**
26703      * Selects the row immediately following the last selected row.
26704      * @param {Boolean} keepExisting (optional) True to keep existing selections
26705      */
26706     selectNext : function(keepExisting)
26707     {
26708             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26709             this.selectRow(this.last+1, keepExisting);
26710             this.grid.getView().focusRow(this.last);
26711         }
26712     },
26713
26714     /**
26715      * Selects the row that precedes the last selected row.
26716      * @param {Boolean} keepExisting (optional) True to keep existing selections
26717      */
26718     selectPrevious : function(keepExisting){
26719         if(this.last){
26720             this.selectRow(this.last-1, keepExisting);
26721             this.grid.getView().focusRow(this.last);
26722         }
26723     },
26724
26725     /**
26726      * Returns the selected records
26727      * @return {Array} Array of selected records
26728      */
26729     getSelections : function(){
26730         return [].concat(this.selections.items);
26731     },
26732
26733     /**
26734      * Returns the first selected record.
26735      * @return {Record}
26736      */
26737     getSelected : function(){
26738         return this.selections.itemAt(0);
26739     },
26740
26741
26742     /**
26743      * Clears all selections.
26744      */
26745     clearSelections : function(fast)
26746     {
26747         if(this.locked) {
26748             return;
26749         }
26750         if(fast !== true){
26751                 var ds = this.grid.store;
26752             var s = this.selections;
26753             s.each(function(r){
26754                 this.deselectRow(ds.indexOfId(r.id));
26755             }, this);
26756             s.clear();
26757         }else{
26758             this.selections.clear();
26759         }
26760         this.last = false;
26761     },
26762
26763
26764     /**
26765      * Selects all rows.
26766      */
26767     selectAll : function(){
26768         if(this.locked) {
26769             return;
26770         }
26771         this.selections.clear();
26772         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26773             this.selectRow(i, true);
26774         }
26775     },
26776
26777     /**
26778      * Returns True if there is a selection.
26779      * @return {Boolean}
26780      */
26781     hasSelection : function(){
26782         return this.selections.length > 0;
26783     },
26784
26785     /**
26786      * Returns True if the specified row is selected.
26787      * @param {Number/Record} record The record or index of the record to check
26788      * @return {Boolean}
26789      */
26790     isSelected : function(index){
26791             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26792         return (r && this.selections.key(r.id) ? true : false);
26793     },
26794
26795     /**
26796      * Returns True if the specified record id is selected.
26797      * @param {String} id The id of record to check
26798      * @return {Boolean}
26799      */
26800     isIdSelected : function(id){
26801         return (this.selections.key(id) ? true : false);
26802     },
26803
26804
26805     // private
26806     handleMouseDBClick : function(e, t){
26807         
26808     },
26809     // private
26810     handleMouseDown : function(e, t)
26811     {
26812             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26813         if(this.isLocked() || rowIndex < 0 ){
26814             return;
26815         };
26816         if(e.shiftKey && this.last !== false){
26817             var last = this.last;
26818             this.selectRange(last, rowIndex, e.ctrlKey);
26819             this.last = last; // reset the last
26820             t.focus();
26821     
26822         }else{
26823             var isSelected = this.isSelected(rowIndex);
26824             //Roo.log("select row:" + rowIndex);
26825             if(isSelected){
26826                 this.deselectRow(rowIndex);
26827             } else {
26828                         this.selectRow(rowIndex, true);
26829             }
26830     
26831             /*
26832                 if(e.button !== 0 && isSelected){
26833                 alert('rowIndex 2: ' + rowIndex);
26834                     view.focusRow(rowIndex);
26835                 }else if(e.ctrlKey && isSelected){
26836                     this.deselectRow(rowIndex);
26837                 }else if(!isSelected){
26838                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26839                     view.focusRow(rowIndex);
26840                 }
26841             */
26842         }
26843         this.fireEvent("afterselectionchange", this);
26844     },
26845     // private
26846     handleDragableRowClick :  function(grid, rowIndex, e) 
26847     {
26848         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26849             this.selectRow(rowIndex, false);
26850             grid.view.focusRow(rowIndex);
26851              this.fireEvent("afterselectionchange", this);
26852         }
26853     },
26854     
26855     /**
26856      * Selects multiple rows.
26857      * @param {Array} rows Array of the indexes of the row to select
26858      * @param {Boolean} keepExisting (optional) True to keep existing selections
26859      */
26860     selectRows : function(rows, keepExisting){
26861         if(!keepExisting){
26862             this.clearSelections();
26863         }
26864         for(var i = 0, len = rows.length; i < len; i++){
26865             this.selectRow(rows[i], true);
26866         }
26867     },
26868
26869     /**
26870      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26871      * @param {Number} startRow The index of the first row in the range
26872      * @param {Number} endRow The index of the last row in the range
26873      * @param {Boolean} keepExisting (optional) True to retain existing selections
26874      */
26875     selectRange : function(startRow, endRow, keepExisting){
26876         if(this.locked) {
26877             return;
26878         }
26879         if(!keepExisting){
26880             this.clearSelections();
26881         }
26882         if(startRow <= endRow){
26883             for(var i = startRow; i <= endRow; i++){
26884                 this.selectRow(i, true);
26885             }
26886         }else{
26887             for(var i = startRow; i >= endRow; i--){
26888                 this.selectRow(i, true);
26889             }
26890         }
26891     },
26892
26893     /**
26894      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26895      * @param {Number} startRow The index of the first row in the range
26896      * @param {Number} endRow The index of the last row in the range
26897      */
26898     deselectRange : function(startRow, endRow, preventViewNotify){
26899         if(this.locked) {
26900             return;
26901         }
26902         for(var i = startRow; i <= endRow; i++){
26903             this.deselectRow(i, preventViewNotify);
26904         }
26905     },
26906
26907     /**
26908      * Selects a row.
26909      * @param {Number} row The index of the row to select
26910      * @param {Boolean} keepExisting (optional) True to keep existing selections
26911      */
26912     selectRow : function(index, keepExisting, preventViewNotify)
26913     {
26914             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26915             return;
26916         }
26917         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26918             if(!keepExisting || this.singleSelect){
26919                 this.clearSelections();
26920             }
26921             
26922             var r = this.grid.store.getAt(index);
26923             //console.log('selectRow - record id :' + r.id);
26924             
26925             this.selections.add(r);
26926             this.last = this.lastActive = index;
26927             if(!preventViewNotify){
26928                 var proxy = new Roo.Element(
26929                                 this.grid.getRowDom(index)
26930                 );
26931                 proxy.addClass('bg-info info');
26932             }
26933             this.fireEvent("rowselect", this, index, r);
26934             this.fireEvent("selectionchange", this);
26935         }
26936     },
26937
26938     /**
26939      * Deselects a row.
26940      * @param {Number} row The index of the row to deselect
26941      */
26942     deselectRow : function(index, preventViewNotify)
26943     {
26944         if(this.locked) {
26945             return;
26946         }
26947         if(this.last == index){
26948             this.last = false;
26949         }
26950         if(this.lastActive == index){
26951             this.lastActive = false;
26952         }
26953         
26954         var r = this.grid.store.getAt(index);
26955         if (!r) {
26956             return;
26957         }
26958         
26959         this.selections.remove(r);
26960         //.console.log('deselectRow - record id :' + r.id);
26961         if(!preventViewNotify){
26962         
26963             var proxy = new Roo.Element(
26964                 this.grid.getRowDom(index)
26965             );
26966             proxy.removeClass('bg-info info');
26967         }
26968         this.fireEvent("rowdeselect", this, index);
26969         this.fireEvent("selectionchange", this);
26970     },
26971
26972     // private
26973     restoreLast : function(){
26974         if(this._last){
26975             this.last = this._last;
26976         }
26977     },
26978
26979     // private
26980     acceptsNav : function(row, col, cm){
26981         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26982     },
26983
26984     // private
26985     onEditorKey : function(field, e){
26986         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26987         if(k == e.TAB){
26988             e.stopEvent();
26989             ed.completeEdit();
26990             if(e.shiftKey){
26991                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26992             }else{
26993                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26994             }
26995         }else if(k == e.ENTER && !e.ctrlKey){
26996             e.stopEvent();
26997             ed.completeEdit();
26998             if(e.shiftKey){
26999                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27000             }else{
27001                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27002             }
27003         }else if(k == e.ESC){
27004             ed.cancelEdit();
27005         }
27006         if(newCell){
27007             g.startEditing(newCell[0], newCell[1]);
27008         }
27009     }
27010 });
27011 /*
27012  * Based on:
27013  * Ext JS Library 1.1.1
27014  * Copyright(c) 2006-2007, Ext JS, LLC.
27015  *
27016  * Originally Released Under LGPL - original licence link has changed is not relivant.
27017  *
27018  * Fork - LGPL
27019  * <script type="text/javascript">
27020  */
27021  
27022 /**
27023  * @class Roo.bootstrap.PagingToolbar
27024  * @extends Roo.bootstrap.NavSimplebar
27025  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27026  * @constructor
27027  * Create a new PagingToolbar
27028  * @param {Object} config The config object
27029  * @param {Roo.data.Store} store
27030  */
27031 Roo.bootstrap.PagingToolbar = function(config)
27032 {
27033     // old args format still supported... - xtype is prefered..
27034         // created from xtype...
27035     
27036     this.ds = config.dataSource;
27037     
27038     if (config.store && !this.ds) {
27039         this.store= Roo.factory(config.store, Roo.data);
27040         this.ds = this.store;
27041         this.ds.xmodule = this.xmodule || false;
27042     }
27043     
27044     this.toolbarItems = [];
27045     if (config.items) {
27046         this.toolbarItems = config.items;
27047     }
27048     
27049     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27050     
27051     this.cursor = 0;
27052     
27053     if (this.ds) { 
27054         this.bind(this.ds);
27055     }
27056     
27057     if (Roo.bootstrap.version == 4) {
27058         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27059     } else {
27060         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27061     }
27062     
27063 };
27064
27065 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27066     /**
27067      * @cfg {Roo.data.Store} dataSource
27068      * The underlying data store providing the paged data
27069      */
27070     /**
27071      * @cfg {String/HTMLElement/Element} container
27072      * container The id or element that will contain the toolbar
27073      */
27074     /**
27075      * @cfg {Boolean} displayInfo
27076      * True to display the displayMsg (defaults to false)
27077      */
27078     /**
27079      * @cfg {Number} pageSize
27080      * The number of records to display per page (defaults to 20)
27081      */
27082     pageSize: 20,
27083     /**
27084      * @cfg {String} displayMsg
27085      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27086      */
27087     displayMsg : 'Displaying {0} - {1} of {2}',
27088     /**
27089      * @cfg {String} emptyMsg
27090      * The message to display when no records are found (defaults to "No data to display")
27091      */
27092     emptyMsg : 'No data to display',
27093     /**
27094      * Customizable piece of the default paging text (defaults to "Page")
27095      * @type String
27096      */
27097     beforePageText : "Page",
27098     /**
27099      * Customizable piece of the default paging text (defaults to "of %0")
27100      * @type String
27101      */
27102     afterPageText : "of {0}",
27103     /**
27104      * Customizable piece of the default paging text (defaults to "First Page")
27105      * @type String
27106      */
27107     firstText : "First Page",
27108     /**
27109      * Customizable piece of the default paging text (defaults to "Previous Page")
27110      * @type String
27111      */
27112     prevText : "Previous Page",
27113     /**
27114      * Customizable piece of the default paging text (defaults to "Next Page")
27115      * @type String
27116      */
27117     nextText : "Next Page",
27118     /**
27119      * Customizable piece of the default paging text (defaults to "Last Page")
27120      * @type String
27121      */
27122     lastText : "Last Page",
27123     /**
27124      * Customizable piece of the default paging text (defaults to "Refresh")
27125      * @type String
27126      */
27127     refreshText : "Refresh",
27128
27129     buttons : false,
27130     // private
27131     onRender : function(ct, position) 
27132     {
27133         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27134         this.navgroup.parentId = this.id;
27135         this.navgroup.onRender(this.el, null);
27136         // add the buttons to the navgroup
27137         
27138         if(this.displayInfo){
27139             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27140             this.displayEl = this.el.select('.x-paging-info', true).first();
27141 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27142 //            this.displayEl = navel.el.select('span',true).first();
27143         }
27144         
27145         var _this = this;
27146         
27147         if(this.buttons){
27148             Roo.each(_this.buttons, function(e){ // this might need to use render????
27149                Roo.factory(e).render(_this.el);
27150             });
27151         }
27152             
27153         Roo.each(_this.toolbarItems, function(e) {
27154             _this.navgroup.addItem(e);
27155         });
27156         
27157         
27158         this.first = this.navgroup.addItem({
27159             tooltip: this.firstText,
27160             cls: "prev btn-outline-secondary",
27161             html : ' <i class="fa fa-step-backward"></i>',
27162             disabled: true,
27163             preventDefault: true,
27164             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27165         });
27166         
27167         this.prev =  this.navgroup.addItem({
27168             tooltip: this.prevText,
27169             cls: "prev btn-outline-secondary",
27170             html : ' <i class="fa fa-backward"></i>',
27171             disabled: true,
27172             preventDefault: true,
27173             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27174         });
27175     //this.addSeparator();
27176         
27177         
27178         var field = this.navgroup.addItem( {
27179             tagtype : 'span',
27180             cls : 'x-paging-position  btn-outline-secondary',
27181              disabled: true,
27182             html : this.beforePageText  +
27183                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27184                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27185          } ); //?? escaped?
27186         
27187         this.field = field.el.select('input', true).first();
27188         this.field.on("keydown", this.onPagingKeydown, this);
27189         this.field.on("focus", function(){this.dom.select();});
27190     
27191     
27192         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27193         //this.field.setHeight(18);
27194         //this.addSeparator();
27195         this.next = this.navgroup.addItem({
27196             tooltip: this.nextText,
27197             cls: "next btn-outline-secondary",
27198             html : ' <i class="fa fa-forward"></i>',
27199             disabled: true,
27200             preventDefault: true,
27201             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27202         });
27203         this.last = this.navgroup.addItem({
27204             tooltip: this.lastText,
27205             html : ' <i class="fa fa-step-forward"></i>',
27206             cls: "next btn-outline-secondary",
27207             disabled: true,
27208             preventDefault: true,
27209             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27210         });
27211     //this.addSeparator();
27212         this.loading = this.navgroup.addItem({
27213             tooltip: this.refreshText,
27214             cls: "btn-outline-secondary",
27215             html : ' <i class="fa fa-refresh"></i>',
27216             preventDefault: true,
27217             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27218         });
27219         
27220     },
27221
27222     // private
27223     updateInfo : function(){
27224         if(this.displayEl){
27225             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27226             var msg = count == 0 ?
27227                 this.emptyMsg :
27228                 String.format(
27229                     this.displayMsg,
27230                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27231                 );
27232             this.displayEl.update(msg);
27233         }
27234     },
27235
27236     // private
27237     onLoad : function(ds, r, o)
27238     {
27239         this.cursor = o.params.start ? o.params.start : 0;
27240         
27241         var d = this.getPageData(),
27242             ap = d.activePage,
27243             ps = d.pages;
27244         
27245         
27246         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27247         this.field.dom.value = ap;
27248         this.first.setDisabled(ap == 1);
27249         this.prev.setDisabled(ap == 1);
27250         this.next.setDisabled(ap == ps);
27251         this.last.setDisabled(ap == ps);
27252         this.loading.enable();
27253         this.updateInfo();
27254     },
27255
27256     // private
27257     getPageData : function(){
27258         var total = this.ds.getTotalCount();
27259         return {
27260             total : total,
27261             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27262             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27263         };
27264     },
27265
27266     // private
27267     onLoadError : function(){
27268         this.loading.enable();
27269     },
27270
27271     // private
27272     onPagingKeydown : function(e){
27273         var k = e.getKey();
27274         var d = this.getPageData();
27275         if(k == e.RETURN){
27276             var v = this.field.dom.value, pageNum;
27277             if(!v || isNaN(pageNum = parseInt(v, 10))){
27278                 this.field.dom.value = d.activePage;
27279                 return;
27280             }
27281             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27282             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27283             e.stopEvent();
27284         }
27285         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))
27286         {
27287           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27288           this.field.dom.value = pageNum;
27289           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27290           e.stopEvent();
27291         }
27292         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27293         {
27294           var v = this.field.dom.value, pageNum; 
27295           var increment = (e.shiftKey) ? 10 : 1;
27296           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27297                 increment *= -1;
27298           }
27299           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27300             this.field.dom.value = d.activePage;
27301             return;
27302           }
27303           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27304           {
27305             this.field.dom.value = parseInt(v, 10) + increment;
27306             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27307             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27308           }
27309           e.stopEvent();
27310         }
27311     },
27312
27313     // private
27314     beforeLoad : function(){
27315         if(this.loading){
27316             this.loading.disable();
27317         }
27318     },
27319
27320     // private
27321     onClick : function(which){
27322         
27323         var ds = this.ds;
27324         if (!ds) {
27325             return;
27326         }
27327         
27328         switch(which){
27329             case "first":
27330                 ds.load({params:{start: 0, limit: this.pageSize}});
27331             break;
27332             case "prev":
27333                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27334             break;
27335             case "next":
27336                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27337             break;
27338             case "last":
27339                 var total = ds.getTotalCount();
27340                 var extra = total % this.pageSize;
27341                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27342                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27343             break;
27344             case "refresh":
27345                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27346             break;
27347         }
27348     },
27349
27350     /**
27351      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27352      * @param {Roo.data.Store} store The data store to unbind
27353      */
27354     unbind : function(ds){
27355         ds.un("beforeload", this.beforeLoad, this);
27356         ds.un("load", this.onLoad, this);
27357         ds.un("loadexception", this.onLoadError, this);
27358         ds.un("remove", this.updateInfo, this);
27359         ds.un("add", this.updateInfo, this);
27360         this.ds = undefined;
27361     },
27362
27363     /**
27364      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27365      * @param {Roo.data.Store} store The data store to bind
27366      */
27367     bind : function(ds){
27368         ds.on("beforeload", this.beforeLoad, this);
27369         ds.on("load", this.onLoad, this);
27370         ds.on("loadexception", this.onLoadError, this);
27371         ds.on("remove", this.updateInfo, this);
27372         ds.on("add", this.updateInfo, this);
27373         this.ds = ds;
27374     }
27375 });/*
27376  * - LGPL
27377  *
27378  * element
27379  * 
27380  */
27381
27382 /**
27383  * @class Roo.bootstrap.MessageBar
27384  * @extends Roo.bootstrap.Component
27385  * Bootstrap MessageBar class
27386  * @cfg {String} html contents of the MessageBar
27387  * @cfg {String} weight (info | success | warning | danger) default info
27388  * @cfg {String} beforeClass insert the bar before the given class
27389  * @cfg {Boolean} closable (true | false) default false
27390  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27391  * 
27392  * @constructor
27393  * Create a new Element
27394  * @param {Object} config The config object
27395  */
27396
27397 Roo.bootstrap.MessageBar = function(config){
27398     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27399 };
27400
27401 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27402     
27403     html: '',
27404     weight: 'info',
27405     closable: false,
27406     fixed: false,
27407     beforeClass: 'bootstrap-sticky-wrap',
27408     
27409     getAutoCreate : function(){
27410         
27411         var cfg = {
27412             tag: 'div',
27413             cls: 'alert alert-dismissable alert-' + this.weight,
27414             cn: [
27415                 {
27416                     tag: 'span',
27417                     cls: 'message',
27418                     html: this.html || ''
27419                 }
27420             ]
27421         };
27422         
27423         if(this.fixed){
27424             cfg.cls += ' alert-messages-fixed';
27425         }
27426         
27427         if(this.closable){
27428             cfg.cn.push({
27429                 tag: 'button',
27430                 cls: 'close',
27431                 html: 'x'
27432             });
27433         }
27434         
27435         return cfg;
27436     },
27437     
27438     onRender : function(ct, position)
27439     {
27440         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27441         
27442         if(!this.el){
27443             var cfg = Roo.apply({},  this.getAutoCreate());
27444             cfg.id = Roo.id();
27445             
27446             if (this.cls) {
27447                 cfg.cls += ' ' + this.cls;
27448             }
27449             if (this.style) {
27450                 cfg.style = this.style;
27451             }
27452             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27453             
27454             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27455         }
27456         
27457         this.el.select('>button.close').on('click', this.hide, this);
27458         
27459     },
27460     
27461     show : function()
27462     {
27463         if (!this.rendered) {
27464             this.render();
27465         }
27466         
27467         this.el.show();
27468         
27469         this.fireEvent('show', this);
27470         
27471     },
27472     
27473     hide : function()
27474     {
27475         if (!this.rendered) {
27476             this.render();
27477         }
27478         
27479         this.el.hide();
27480         
27481         this.fireEvent('hide', this);
27482     },
27483     
27484     update : function()
27485     {
27486 //        var e = this.el.dom.firstChild;
27487 //        
27488 //        if(this.closable){
27489 //            e = e.nextSibling;
27490 //        }
27491 //        
27492 //        e.data = this.html || '';
27493
27494         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27495     }
27496    
27497 });
27498
27499  
27500
27501      /*
27502  * - LGPL
27503  *
27504  * Graph
27505  * 
27506  */
27507
27508
27509 /**
27510  * @class Roo.bootstrap.Graph
27511  * @extends Roo.bootstrap.Component
27512  * Bootstrap Graph class
27513 > Prameters
27514  -sm {number} sm 4
27515  -md {number} md 5
27516  @cfg {String} graphtype  bar | vbar | pie
27517  @cfg {number} g_x coodinator | centre x (pie)
27518  @cfg {number} g_y coodinator | centre y (pie)
27519  @cfg {number} g_r radius (pie)
27520  @cfg {number} g_height height of the chart (respected by all elements in the set)
27521  @cfg {number} g_width width of the chart (respected by all elements in the set)
27522  @cfg {Object} title The title of the chart
27523     
27524  -{Array}  values
27525  -opts (object) options for the chart 
27526      o {
27527      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27528      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27529      o vgutter (number)
27530      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.
27531      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27532      o to
27533      o stretch (boolean)
27534      o }
27535  -opts (object) options for the pie
27536      o{
27537      o cut
27538      o startAngle (number)
27539      o endAngle (number)
27540      } 
27541  *
27542  * @constructor
27543  * Create a new Input
27544  * @param {Object} config The config object
27545  */
27546
27547 Roo.bootstrap.Graph = function(config){
27548     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27549     
27550     this.addEvents({
27551         // img events
27552         /**
27553          * @event click
27554          * The img click event for the img.
27555          * @param {Roo.EventObject} e
27556          */
27557         "click" : true
27558     });
27559 };
27560
27561 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27562     
27563     sm: 4,
27564     md: 5,
27565     graphtype: 'bar',
27566     g_height: 250,
27567     g_width: 400,
27568     g_x: 50,
27569     g_y: 50,
27570     g_r: 30,
27571     opts:{
27572         //g_colors: this.colors,
27573         g_type: 'soft',
27574         g_gutter: '20%'
27575
27576     },
27577     title : false,
27578
27579     getAutoCreate : function(){
27580         
27581         var cfg = {
27582             tag: 'div',
27583             html : null
27584         };
27585         
27586         
27587         return  cfg;
27588     },
27589
27590     onRender : function(ct,position){
27591         
27592         
27593         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27594         
27595         if (typeof(Raphael) == 'undefined') {
27596             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27597             return;
27598         }
27599         
27600         this.raphael = Raphael(this.el.dom);
27601         
27602                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27603                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27604                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27605                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27606                 /*
27607                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27608                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27609                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27610                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27611                 
27612                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27613                 r.barchart(330, 10, 300, 220, data1);
27614                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27615                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27616                 */
27617                 
27618                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27619                 // r.barchart(30, 30, 560, 250,  xdata, {
27620                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27621                 //     axis : "0 0 1 1",
27622                 //     axisxlabels :  xdata
27623                 //     //yvalues : cols,
27624                    
27625                 // });
27626 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27627 //        
27628 //        this.load(null,xdata,{
27629 //                axis : "0 0 1 1",
27630 //                axisxlabels :  xdata
27631 //                });
27632
27633     },
27634
27635     load : function(graphtype,xdata,opts)
27636     {
27637         this.raphael.clear();
27638         if(!graphtype) {
27639             graphtype = this.graphtype;
27640         }
27641         if(!opts){
27642             opts = this.opts;
27643         }
27644         var r = this.raphael,
27645             fin = function () {
27646                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27647             },
27648             fout = function () {
27649                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27650             },
27651             pfin = function() {
27652                 this.sector.stop();
27653                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27654
27655                 if (this.label) {
27656                     this.label[0].stop();
27657                     this.label[0].attr({ r: 7.5 });
27658                     this.label[1].attr({ "font-weight": 800 });
27659                 }
27660             },
27661             pfout = function() {
27662                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27663
27664                 if (this.label) {
27665                     this.label[0].animate({ r: 5 }, 500, "bounce");
27666                     this.label[1].attr({ "font-weight": 400 });
27667                 }
27668             };
27669
27670         switch(graphtype){
27671             case 'bar':
27672                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27673                 break;
27674             case 'hbar':
27675                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27676                 break;
27677             case 'pie':
27678 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27679 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27680 //            
27681                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27682                 
27683                 break;
27684
27685         }
27686         
27687         if(this.title){
27688             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27689         }
27690         
27691     },
27692     
27693     setTitle: function(o)
27694     {
27695         this.title = o;
27696     },
27697     
27698     initEvents: function() {
27699         
27700         if(!this.href){
27701             this.el.on('click', this.onClick, this);
27702         }
27703     },
27704     
27705     onClick : function(e)
27706     {
27707         Roo.log('img onclick');
27708         this.fireEvent('click', this, e);
27709     }
27710    
27711 });
27712
27713  
27714 /*
27715  * - LGPL
27716  *
27717  * numberBox
27718  * 
27719  */
27720 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27721
27722 /**
27723  * @class Roo.bootstrap.dash.NumberBox
27724  * @extends Roo.bootstrap.Component
27725  * Bootstrap NumberBox class
27726  * @cfg {String} headline Box headline
27727  * @cfg {String} content Box content
27728  * @cfg {String} icon Box icon
27729  * @cfg {String} footer Footer text
27730  * @cfg {String} fhref Footer href
27731  * 
27732  * @constructor
27733  * Create a new NumberBox
27734  * @param {Object} config The config object
27735  */
27736
27737
27738 Roo.bootstrap.dash.NumberBox = function(config){
27739     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27740     
27741 };
27742
27743 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27744     
27745     headline : '',
27746     content : '',
27747     icon : '',
27748     footer : '',
27749     fhref : '',
27750     ficon : '',
27751     
27752     getAutoCreate : function(){
27753         
27754         var cfg = {
27755             tag : 'div',
27756             cls : 'small-box ',
27757             cn : [
27758                 {
27759                     tag : 'div',
27760                     cls : 'inner',
27761                     cn :[
27762                         {
27763                             tag : 'h3',
27764                             cls : 'roo-headline',
27765                             html : this.headline
27766                         },
27767                         {
27768                             tag : 'p',
27769                             cls : 'roo-content',
27770                             html : this.content
27771                         }
27772                     ]
27773                 }
27774             ]
27775         };
27776         
27777         if(this.icon){
27778             cfg.cn.push({
27779                 tag : 'div',
27780                 cls : 'icon',
27781                 cn :[
27782                     {
27783                         tag : 'i',
27784                         cls : 'ion ' + this.icon
27785                     }
27786                 ]
27787             });
27788         }
27789         
27790         if(this.footer){
27791             var footer = {
27792                 tag : 'a',
27793                 cls : 'small-box-footer',
27794                 href : this.fhref || '#',
27795                 html : this.footer
27796             };
27797             
27798             cfg.cn.push(footer);
27799             
27800         }
27801         
27802         return  cfg;
27803     },
27804
27805     onRender : function(ct,position){
27806         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27807
27808
27809        
27810                 
27811     },
27812
27813     setHeadline: function (value)
27814     {
27815         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27816     },
27817     
27818     setFooter: function (value, href)
27819     {
27820         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27821         
27822         if(href){
27823             this.el.select('a.small-box-footer',true).first().attr('href', href);
27824         }
27825         
27826     },
27827
27828     setContent: function (value)
27829     {
27830         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27831     },
27832
27833     initEvents: function() 
27834     {   
27835         
27836     }
27837     
27838 });
27839
27840  
27841 /*
27842  * - LGPL
27843  *
27844  * TabBox
27845  * 
27846  */
27847 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27848
27849 /**
27850  * @class Roo.bootstrap.dash.TabBox
27851  * @extends Roo.bootstrap.Component
27852  * Bootstrap TabBox class
27853  * @cfg {String} title Title of the TabBox
27854  * @cfg {String} icon Icon of the TabBox
27855  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27856  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27857  * 
27858  * @constructor
27859  * Create a new TabBox
27860  * @param {Object} config The config object
27861  */
27862
27863
27864 Roo.bootstrap.dash.TabBox = function(config){
27865     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27866     this.addEvents({
27867         // raw events
27868         /**
27869          * @event addpane
27870          * When a pane is added
27871          * @param {Roo.bootstrap.dash.TabPane} pane
27872          */
27873         "addpane" : true,
27874         /**
27875          * @event activatepane
27876          * When a pane is activated
27877          * @param {Roo.bootstrap.dash.TabPane} pane
27878          */
27879         "activatepane" : true
27880         
27881          
27882     });
27883     
27884     this.panes = [];
27885 };
27886
27887 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27888
27889     title : '',
27890     icon : false,
27891     showtabs : true,
27892     tabScrollable : false,
27893     
27894     getChildContainer : function()
27895     {
27896         return this.el.select('.tab-content', true).first();
27897     },
27898     
27899     getAutoCreate : function(){
27900         
27901         var header = {
27902             tag: 'li',
27903             cls: 'pull-left header',
27904             html: this.title,
27905             cn : []
27906         };
27907         
27908         if(this.icon){
27909             header.cn.push({
27910                 tag: 'i',
27911                 cls: 'fa ' + this.icon
27912             });
27913         }
27914         
27915         var h = {
27916             tag: 'ul',
27917             cls: 'nav nav-tabs pull-right',
27918             cn: [
27919                 header
27920             ]
27921         };
27922         
27923         if(this.tabScrollable){
27924             h = {
27925                 tag: 'div',
27926                 cls: 'tab-header',
27927                 cn: [
27928                     {
27929                         tag: 'ul',
27930                         cls: 'nav nav-tabs pull-right',
27931                         cn: [
27932                             header
27933                         ]
27934                     }
27935                 ]
27936             };
27937         }
27938         
27939         var cfg = {
27940             tag: 'div',
27941             cls: 'nav-tabs-custom',
27942             cn: [
27943                 h,
27944                 {
27945                     tag: 'div',
27946                     cls: 'tab-content no-padding',
27947                     cn: []
27948                 }
27949             ]
27950         };
27951
27952         return  cfg;
27953     },
27954     initEvents : function()
27955     {
27956         //Roo.log('add add pane handler');
27957         this.on('addpane', this.onAddPane, this);
27958     },
27959      /**
27960      * Updates the box title
27961      * @param {String} html to set the title to.
27962      */
27963     setTitle : function(value)
27964     {
27965         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27966     },
27967     onAddPane : function(pane)
27968     {
27969         this.panes.push(pane);
27970         //Roo.log('addpane');
27971         //Roo.log(pane);
27972         // tabs are rendere left to right..
27973         if(!this.showtabs){
27974             return;
27975         }
27976         
27977         var ctr = this.el.select('.nav-tabs', true).first();
27978          
27979          
27980         var existing = ctr.select('.nav-tab',true);
27981         var qty = existing.getCount();;
27982         
27983         
27984         var tab = ctr.createChild({
27985             tag : 'li',
27986             cls : 'nav-tab' + (qty ? '' : ' active'),
27987             cn : [
27988                 {
27989                     tag : 'a',
27990                     href:'#',
27991                     html : pane.title
27992                 }
27993             ]
27994         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27995         pane.tab = tab;
27996         
27997         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27998         if (!qty) {
27999             pane.el.addClass('active');
28000         }
28001         
28002                 
28003     },
28004     onTabClick : function(ev,un,ob,pane)
28005     {
28006         //Roo.log('tab - prev default');
28007         ev.preventDefault();
28008         
28009         
28010         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28011         pane.tab.addClass('active');
28012         //Roo.log(pane.title);
28013         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28014         // technically we should have a deactivate event.. but maybe add later.
28015         // and it should not de-activate the selected tab...
28016         this.fireEvent('activatepane', pane);
28017         pane.el.addClass('active');
28018         pane.fireEvent('activate');
28019         
28020         
28021     },
28022     
28023     getActivePane : function()
28024     {
28025         var r = false;
28026         Roo.each(this.panes, function(p) {
28027             if(p.el.hasClass('active')){
28028                 r = p;
28029                 return false;
28030             }
28031             
28032             return;
28033         });
28034         
28035         return r;
28036     }
28037     
28038     
28039 });
28040
28041  
28042 /*
28043  * - LGPL
28044  *
28045  * Tab pane
28046  * 
28047  */
28048 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28049 /**
28050  * @class Roo.bootstrap.TabPane
28051  * @extends Roo.bootstrap.Component
28052  * Bootstrap TabPane class
28053  * @cfg {Boolean} active (false | true) Default false
28054  * @cfg {String} title title of panel
28055
28056  * 
28057  * @constructor
28058  * Create a new TabPane
28059  * @param {Object} config The config object
28060  */
28061
28062 Roo.bootstrap.dash.TabPane = function(config){
28063     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28064     
28065     this.addEvents({
28066         // raw events
28067         /**
28068          * @event activate
28069          * When a pane is activated
28070          * @param {Roo.bootstrap.dash.TabPane} pane
28071          */
28072         "activate" : true
28073          
28074     });
28075 };
28076
28077 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28078     
28079     active : false,
28080     title : '',
28081     
28082     // the tabBox that this is attached to.
28083     tab : false,
28084      
28085     getAutoCreate : function() 
28086     {
28087         var cfg = {
28088             tag: 'div',
28089             cls: 'tab-pane'
28090         };
28091         
28092         if(this.active){
28093             cfg.cls += ' active';
28094         }
28095         
28096         return cfg;
28097     },
28098     initEvents  : function()
28099     {
28100         //Roo.log('trigger add pane handler');
28101         this.parent().fireEvent('addpane', this)
28102     },
28103     
28104      /**
28105      * Updates the tab title 
28106      * @param {String} html to set the title to.
28107      */
28108     setTitle: function(str)
28109     {
28110         if (!this.tab) {
28111             return;
28112         }
28113         this.title = str;
28114         this.tab.select('a', true).first().dom.innerHTML = str;
28115         
28116     }
28117     
28118     
28119     
28120 });
28121
28122  
28123
28124
28125  /*
28126  * - LGPL
28127  *
28128  * menu
28129  * 
28130  */
28131 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28132
28133 /**
28134  * @class Roo.bootstrap.menu.Menu
28135  * @extends Roo.bootstrap.Component
28136  * Bootstrap Menu class - container for Menu
28137  * @cfg {String} html Text of the menu
28138  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28139  * @cfg {String} icon Font awesome icon
28140  * @cfg {String} pos Menu align to (top | bottom) default bottom
28141  * 
28142  * 
28143  * @constructor
28144  * Create a new Menu
28145  * @param {Object} config The config object
28146  */
28147
28148
28149 Roo.bootstrap.menu.Menu = function(config){
28150     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28151     
28152     this.addEvents({
28153         /**
28154          * @event beforeshow
28155          * Fires before this menu is displayed
28156          * @param {Roo.bootstrap.menu.Menu} this
28157          */
28158         beforeshow : true,
28159         /**
28160          * @event beforehide
28161          * Fires before this menu is hidden
28162          * @param {Roo.bootstrap.menu.Menu} this
28163          */
28164         beforehide : true,
28165         /**
28166          * @event show
28167          * Fires after this menu is displayed
28168          * @param {Roo.bootstrap.menu.Menu} this
28169          */
28170         show : true,
28171         /**
28172          * @event hide
28173          * Fires after this menu is hidden
28174          * @param {Roo.bootstrap.menu.Menu} this
28175          */
28176         hide : true,
28177         /**
28178          * @event click
28179          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28180          * @param {Roo.bootstrap.menu.Menu} this
28181          * @param {Roo.EventObject} e
28182          */
28183         click : true
28184     });
28185     
28186 };
28187
28188 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28189     
28190     submenu : false,
28191     html : '',
28192     weight : 'default',
28193     icon : false,
28194     pos : 'bottom',
28195     
28196     
28197     getChildContainer : function() {
28198         if(this.isSubMenu){
28199             return this.el;
28200         }
28201         
28202         return this.el.select('ul.dropdown-menu', true).first();  
28203     },
28204     
28205     getAutoCreate : function()
28206     {
28207         var text = [
28208             {
28209                 tag : 'span',
28210                 cls : 'roo-menu-text',
28211                 html : this.html
28212             }
28213         ];
28214         
28215         if(this.icon){
28216             text.unshift({
28217                 tag : 'i',
28218                 cls : 'fa ' + this.icon
28219             })
28220         }
28221         
28222         
28223         var cfg = {
28224             tag : 'div',
28225             cls : 'btn-group',
28226             cn : [
28227                 {
28228                     tag : 'button',
28229                     cls : 'dropdown-button btn btn-' + this.weight,
28230                     cn : text
28231                 },
28232                 {
28233                     tag : 'button',
28234                     cls : 'dropdown-toggle btn btn-' + this.weight,
28235                     cn : [
28236                         {
28237                             tag : 'span',
28238                             cls : 'caret'
28239                         }
28240                     ]
28241                 },
28242                 {
28243                     tag : 'ul',
28244                     cls : 'dropdown-menu'
28245                 }
28246             ]
28247             
28248         };
28249         
28250         if(this.pos == 'top'){
28251             cfg.cls += ' dropup';
28252         }
28253         
28254         if(this.isSubMenu){
28255             cfg = {
28256                 tag : 'ul',
28257                 cls : 'dropdown-menu'
28258             }
28259         }
28260         
28261         return cfg;
28262     },
28263     
28264     onRender : function(ct, position)
28265     {
28266         this.isSubMenu = ct.hasClass('dropdown-submenu');
28267         
28268         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28269     },
28270     
28271     initEvents : function() 
28272     {
28273         if(this.isSubMenu){
28274             return;
28275         }
28276         
28277         this.hidden = true;
28278         
28279         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28280         this.triggerEl.on('click', this.onTriggerPress, this);
28281         
28282         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28283         this.buttonEl.on('click', this.onClick, this);
28284         
28285     },
28286     
28287     list : function()
28288     {
28289         if(this.isSubMenu){
28290             return this.el;
28291         }
28292         
28293         return this.el.select('ul.dropdown-menu', true).first();
28294     },
28295     
28296     onClick : function(e)
28297     {
28298         this.fireEvent("click", this, e);
28299     },
28300     
28301     onTriggerPress  : function(e)
28302     {   
28303         if (this.isVisible()) {
28304             this.hide();
28305         } else {
28306             this.show();
28307         }
28308     },
28309     
28310     isVisible : function(){
28311         return !this.hidden;
28312     },
28313     
28314     show : function()
28315     {
28316         this.fireEvent("beforeshow", this);
28317         
28318         this.hidden = false;
28319         this.el.addClass('open');
28320         
28321         Roo.get(document).on("mouseup", this.onMouseUp, this);
28322         
28323         this.fireEvent("show", this);
28324         
28325         
28326     },
28327     
28328     hide : function()
28329     {
28330         this.fireEvent("beforehide", this);
28331         
28332         this.hidden = true;
28333         this.el.removeClass('open');
28334         
28335         Roo.get(document).un("mouseup", this.onMouseUp);
28336         
28337         this.fireEvent("hide", this);
28338     },
28339     
28340     onMouseUp : function()
28341     {
28342         this.hide();
28343     }
28344     
28345 });
28346
28347  
28348  /*
28349  * - LGPL
28350  *
28351  * menu item
28352  * 
28353  */
28354 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28355
28356 /**
28357  * @class Roo.bootstrap.menu.Item
28358  * @extends Roo.bootstrap.Component
28359  * Bootstrap MenuItem class
28360  * @cfg {Boolean} submenu (true | false) default false
28361  * @cfg {String} html text of the item
28362  * @cfg {String} href the link
28363  * @cfg {Boolean} disable (true | false) default false
28364  * @cfg {Boolean} preventDefault (true | false) default true
28365  * @cfg {String} icon Font awesome icon
28366  * @cfg {String} pos Submenu align to (left | right) default right 
28367  * 
28368  * 
28369  * @constructor
28370  * Create a new Item
28371  * @param {Object} config The config object
28372  */
28373
28374
28375 Roo.bootstrap.menu.Item = function(config){
28376     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28377     this.addEvents({
28378         /**
28379          * @event mouseover
28380          * Fires when the mouse is hovering over this menu
28381          * @param {Roo.bootstrap.menu.Item} this
28382          * @param {Roo.EventObject} e
28383          */
28384         mouseover : true,
28385         /**
28386          * @event mouseout
28387          * Fires when the mouse exits this menu
28388          * @param {Roo.bootstrap.menu.Item} this
28389          * @param {Roo.EventObject} e
28390          */
28391         mouseout : true,
28392         // raw events
28393         /**
28394          * @event click
28395          * The raw click event for the entire grid.
28396          * @param {Roo.EventObject} e
28397          */
28398         click : true
28399     });
28400 };
28401
28402 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28403     
28404     submenu : false,
28405     href : '',
28406     html : '',
28407     preventDefault: true,
28408     disable : false,
28409     icon : false,
28410     pos : 'right',
28411     
28412     getAutoCreate : function()
28413     {
28414         var text = [
28415             {
28416                 tag : 'span',
28417                 cls : 'roo-menu-item-text',
28418                 html : this.html
28419             }
28420         ];
28421         
28422         if(this.icon){
28423             text.unshift({
28424                 tag : 'i',
28425                 cls : 'fa ' + this.icon
28426             })
28427         }
28428         
28429         var cfg = {
28430             tag : 'li',
28431             cn : [
28432                 {
28433                     tag : 'a',
28434                     href : this.href || '#',
28435                     cn : text
28436                 }
28437             ]
28438         };
28439         
28440         if(this.disable){
28441             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28442         }
28443         
28444         if(this.submenu){
28445             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28446             
28447             if(this.pos == 'left'){
28448                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28449             }
28450         }
28451         
28452         return cfg;
28453     },
28454     
28455     initEvents : function() 
28456     {
28457         this.el.on('mouseover', this.onMouseOver, this);
28458         this.el.on('mouseout', this.onMouseOut, this);
28459         
28460         this.el.select('a', true).first().on('click', this.onClick, this);
28461         
28462     },
28463     
28464     onClick : function(e)
28465     {
28466         if(this.preventDefault){
28467             e.preventDefault();
28468         }
28469         
28470         this.fireEvent("click", this, e);
28471     },
28472     
28473     onMouseOver : function(e)
28474     {
28475         if(this.submenu && this.pos == 'left'){
28476             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28477         }
28478         
28479         this.fireEvent("mouseover", this, e);
28480     },
28481     
28482     onMouseOut : function(e)
28483     {
28484         this.fireEvent("mouseout", this, e);
28485     }
28486 });
28487
28488  
28489
28490  /*
28491  * - LGPL
28492  *
28493  * menu separator
28494  * 
28495  */
28496 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28497
28498 /**
28499  * @class Roo.bootstrap.menu.Separator
28500  * @extends Roo.bootstrap.Component
28501  * Bootstrap Separator class
28502  * 
28503  * @constructor
28504  * Create a new Separator
28505  * @param {Object} config The config object
28506  */
28507
28508
28509 Roo.bootstrap.menu.Separator = function(config){
28510     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28511 };
28512
28513 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28514     
28515     getAutoCreate : function(){
28516         var cfg = {
28517             tag : 'li',
28518             cls: 'divider'
28519         };
28520         
28521         return cfg;
28522     }
28523    
28524 });
28525
28526  
28527
28528  /*
28529  * - LGPL
28530  *
28531  * Tooltip
28532  * 
28533  */
28534
28535 /**
28536  * @class Roo.bootstrap.Tooltip
28537  * Bootstrap Tooltip class
28538  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28539  * to determine which dom element triggers the tooltip.
28540  * 
28541  * It needs to add support for additional attributes like tooltip-position
28542  * 
28543  * @constructor
28544  * Create a new Toolti
28545  * @param {Object} config The config object
28546  */
28547
28548 Roo.bootstrap.Tooltip = function(config){
28549     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28550     
28551     this.alignment = Roo.bootstrap.Tooltip.alignment;
28552     
28553     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28554         this.alignment = config.alignment;
28555     }
28556     
28557 };
28558
28559 Roo.apply(Roo.bootstrap.Tooltip, {
28560     /**
28561      * @function init initialize tooltip monitoring.
28562      * @static
28563      */
28564     currentEl : false,
28565     currentTip : false,
28566     currentRegion : false,
28567     
28568     //  init : delay?
28569     
28570     init : function()
28571     {
28572         Roo.get(document).on('mouseover', this.enter ,this);
28573         Roo.get(document).on('mouseout', this.leave, this);
28574          
28575         
28576         this.currentTip = new Roo.bootstrap.Tooltip();
28577     },
28578     
28579     enter : function(ev)
28580     {
28581         var dom = ev.getTarget();
28582         
28583         //Roo.log(['enter',dom]);
28584         var el = Roo.fly(dom);
28585         if (this.currentEl) {
28586             //Roo.log(dom);
28587             //Roo.log(this.currentEl);
28588             //Roo.log(this.currentEl.contains(dom));
28589             if (this.currentEl == el) {
28590                 return;
28591             }
28592             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28593                 return;
28594             }
28595
28596         }
28597         
28598         if (this.currentTip.el) {
28599             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28600         }    
28601         //Roo.log(ev);
28602         
28603         if(!el || el.dom == document){
28604             return;
28605         }
28606         
28607         var bindEl = el;
28608         
28609         // you can not look for children, as if el is the body.. then everythign is the child..
28610         if (!el.attr('tooltip')) { //
28611             if (!el.select("[tooltip]").elements.length) {
28612                 return;
28613             }
28614             // is the mouse over this child...?
28615             bindEl = el.select("[tooltip]").first();
28616             var xy = ev.getXY();
28617             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28618                 //Roo.log("not in region.");
28619                 return;
28620             }
28621             //Roo.log("child element over..");
28622             
28623         }
28624         this.currentEl = bindEl;
28625         this.currentTip.bind(bindEl);
28626         this.currentRegion = Roo.lib.Region.getRegion(dom);
28627         this.currentTip.enter();
28628         
28629     },
28630     leave : function(ev)
28631     {
28632         var dom = ev.getTarget();
28633         //Roo.log(['leave',dom]);
28634         if (!this.currentEl) {
28635             return;
28636         }
28637         
28638         
28639         if (dom != this.currentEl.dom) {
28640             return;
28641         }
28642         var xy = ev.getXY();
28643         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28644             return;
28645         }
28646         // only activate leave if mouse cursor is outside... bounding box..
28647         
28648         
28649         
28650         
28651         if (this.currentTip) {
28652             this.currentTip.leave();
28653         }
28654         //Roo.log('clear currentEl');
28655         this.currentEl = false;
28656         
28657         
28658     },
28659     alignment : {
28660         'left' : ['r-l', [-2,0], 'right'],
28661         'right' : ['l-r', [2,0], 'left'],
28662         'bottom' : ['t-b', [0,2], 'top'],
28663         'top' : [ 'b-t', [0,-2], 'bottom']
28664     }
28665     
28666 });
28667
28668
28669 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28670     
28671     
28672     bindEl : false,
28673     
28674     delay : null, // can be { show : 300 , hide: 500}
28675     
28676     timeout : null,
28677     
28678     hoverState : null, //???
28679     
28680     placement : 'bottom', 
28681     
28682     alignment : false,
28683     
28684     getAutoCreate : function(){
28685     
28686         var cfg = {
28687            cls : 'tooltip',   
28688            role : 'tooltip',
28689            cn : [
28690                 {
28691                     cls : 'tooltip-arrow arrow'
28692                 },
28693                 {
28694                     cls : 'tooltip-inner'
28695                 }
28696            ]
28697         };
28698         
28699         return cfg;
28700     },
28701     bind : function(el)
28702     {
28703         this.bindEl = el;
28704     },
28705     
28706     initEvents : function()
28707     {
28708         this.arrowEl = this.el.select('.arrow', true).first();
28709         this.innerEl = this.el.select('.tooltip-inner', true).first();
28710     },
28711     
28712     enter : function () {
28713        
28714         if (this.timeout != null) {
28715             clearTimeout(this.timeout);
28716         }
28717         
28718         this.hoverState = 'in';
28719          //Roo.log("enter - show");
28720         if (!this.delay || !this.delay.show) {
28721             this.show();
28722             return;
28723         }
28724         var _t = this;
28725         this.timeout = setTimeout(function () {
28726             if (_t.hoverState == 'in') {
28727                 _t.show();
28728             }
28729         }, this.delay.show);
28730     },
28731     leave : function()
28732     {
28733         clearTimeout(this.timeout);
28734     
28735         this.hoverState = 'out';
28736          if (!this.delay || !this.delay.hide) {
28737             this.hide();
28738             return;
28739         }
28740        
28741         var _t = this;
28742         this.timeout = setTimeout(function () {
28743             //Roo.log("leave - timeout");
28744             
28745             if (_t.hoverState == 'out') {
28746                 _t.hide();
28747                 Roo.bootstrap.Tooltip.currentEl = false;
28748             }
28749         }, delay);
28750     },
28751     
28752     show : function (msg)
28753     {
28754         if (!this.el) {
28755             this.render(document.body);
28756         }
28757         // set content.
28758         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28759         
28760         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28761         
28762         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28763         
28764         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28765                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28766         
28767         var placement = typeof this.placement == 'function' ?
28768             this.placement.call(this, this.el, on_el) :
28769             this.placement;
28770             
28771         var autoToken = /\s?auto?\s?/i;
28772         var autoPlace = autoToken.test(placement);
28773         if (autoPlace) {
28774             placement = placement.replace(autoToken, '') || 'top';
28775         }
28776         
28777         //this.el.detach()
28778         //this.el.setXY([0,0]);
28779         this.el.show();
28780         //this.el.dom.style.display='block';
28781         
28782         //this.el.appendTo(on_el);
28783         
28784         var p = this.getPosition();
28785         var box = this.el.getBox();
28786         
28787         if (autoPlace) {
28788             // fixme..
28789         }
28790         
28791         var align = this.alignment[placement];
28792         
28793         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28794         
28795         if(placement == 'top' || placement == 'bottom'){
28796             if(xy[0] < 0){
28797                 placement = 'right';
28798             }
28799             
28800             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28801                 placement = 'left';
28802             }
28803             
28804             var scroll = Roo.select('body', true).first().getScroll();
28805             
28806             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28807                 placement = 'top';
28808             }
28809             
28810             align = this.alignment[placement];
28811             
28812             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28813             
28814         }
28815         
28816         this.el.alignTo(this.bindEl, align[0],align[1]);
28817         //var arrow = this.el.select('.arrow',true).first();
28818         //arrow.set(align[2], 
28819         
28820         this.el.addClass(placement);
28821         this.el.addClass("bs-tooltip-"+ placement);
28822         
28823         this.el.addClass('in fade show');
28824         
28825         this.hoverState = null;
28826         
28827         if (this.el.hasClass('fade')) {
28828             // fade it?
28829         }
28830         
28831         
28832         
28833         
28834         
28835     },
28836     hide : function()
28837     {
28838          
28839         if (!this.el) {
28840             return;
28841         }
28842         //this.el.setXY([0,0]);
28843         this.el.removeClass(['show', 'in']);
28844         //this.el.hide();
28845         
28846     }
28847     
28848 });
28849  
28850
28851  /*
28852  * - LGPL
28853  *
28854  * Location Picker
28855  * 
28856  */
28857
28858 /**
28859  * @class Roo.bootstrap.LocationPicker
28860  * @extends Roo.bootstrap.Component
28861  * Bootstrap LocationPicker class
28862  * @cfg {Number} latitude Position when init default 0
28863  * @cfg {Number} longitude Position when init default 0
28864  * @cfg {Number} zoom default 15
28865  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28866  * @cfg {Boolean} mapTypeControl default false
28867  * @cfg {Boolean} disableDoubleClickZoom default false
28868  * @cfg {Boolean} scrollwheel default true
28869  * @cfg {Boolean} streetViewControl default false
28870  * @cfg {Number} radius default 0
28871  * @cfg {String} locationName
28872  * @cfg {Boolean} draggable default true
28873  * @cfg {Boolean} enableAutocomplete default false
28874  * @cfg {Boolean} enableReverseGeocode default true
28875  * @cfg {String} markerTitle
28876  * 
28877  * @constructor
28878  * Create a new LocationPicker
28879  * @param {Object} config The config object
28880  */
28881
28882
28883 Roo.bootstrap.LocationPicker = function(config){
28884     
28885     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28886     
28887     this.addEvents({
28888         /**
28889          * @event initial
28890          * Fires when the picker initialized.
28891          * @param {Roo.bootstrap.LocationPicker} this
28892          * @param {Google Location} location
28893          */
28894         initial : true,
28895         /**
28896          * @event positionchanged
28897          * Fires when the picker position changed.
28898          * @param {Roo.bootstrap.LocationPicker} this
28899          * @param {Google Location} location
28900          */
28901         positionchanged : true,
28902         /**
28903          * @event resize
28904          * Fires when the map resize.
28905          * @param {Roo.bootstrap.LocationPicker} this
28906          */
28907         resize : true,
28908         /**
28909          * @event show
28910          * Fires when the map show.
28911          * @param {Roo.bootstrap.LocationPicker} this
28912          */
28913         show : true,
28914         /**
28915          * @event hide
28916          * Fires when the map hide.
28917          * @param {Roo.bootstrap.LocationPicker} this
28918          */
28919         hide : true,
28920         /**
28921          * @event mapClick
28922          * Fires when click the map.
28923          * @param {Roo.bootstrap.LocationPicker} this
28924          * @param {Map event} e
28925          */
28926         mapClick : true,
28927         /**
28928          * @event mapRightClick
28929          * Fires when right click the map.
28930          * @param {Roo.bootstrap.LocationPicker} this
28931          * @param {Map event} e
28932          */
28933         mapRightClick : true,
28934         /**
28935          * @event markerClick
28936          * Fires when click the marker.
28937          * @param {Roo.bootstrap.LocationPicker} this
28938          * @param {Map event} e
28939          */
28940         markerClick : true,
28941         /**
28942          * @event markerRightClick
28943          * Fires when right click the marker.
28944          * @param {Roo.bootstrap.LocationPicker} this
28945          * @param {Map event} e
28946          */
28947         markerRightClick : true,
28948         /**
28949          * @event OverlayViewDraw
28950          * Fires when OverlayView Draw
28951          * @param {Roo.bootstrap.LocationPicker} this
28952          */
28953         OverlayViewDraw : true,
28954         /**
28955          * @event OverlayViewOnAdd
28956          * Fires when OverlayView Draw
28957          * @param {Roo.bootstrap.LocationPicker} this
28958          */
28959         OverlayViewOnAdd : true,
28960         /**
28961          * @event OverlayViewOnRemove
28962          * Fires when OverlayView Draw
28963          * @param {Roo.bootstrap.LocationPicker} this
28964          */
28965         OverlayViewOnRemove : true,
28966         /**
28967          * @event OverlayViewShow
28968          * Fires when OverlayView Draw
28969          * @param {Roo.bootstrap.LocationPicker} this
28970          * @param {Pixel} cpx
28971          */
28972         OverlayViewShow : true,
28973         /**
28974          * @event OverlayViewHide
28975          * Fires when OverlayView Draw
28976          * @param {Roo.bootstrap.LocationPicker} this
28977          */
28978         OverlayViewHide : true,
28979         /**
28980          * @event loadexception
28981          * Fires when load google lib failed.
28982          * @param {Roo.bootstrap.LocationPicker} this
28983          */
28984         loadexception : true
28985     });
28986         
28987 };
28988
28989 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28990     
28991     gMapContext: false,
28992     
28993     latitude: 0,
28994     longitude: 0,
28995     zoom: 15,
28996     mapTypeId: false,
28997     mapTypeControl: false,
28998     disableDoubleClickZoom: false,
28999     scrollwheel: true,
29000     streetViewControl: false,
29001     radius: 0,
29002     locationName: '',
29003     draggable: true,
29004     enableAutocomplete: false,
29005     enableReverseGeocode: true,
29006     markerTitle: '',
29007     
29008     getAutoCreate: function()
29009     {
29010
29011         var cfg = {
29012             tag: 'div',
29013             cls: 'roo-location-picker'
29014         };
29015         
29016         return cfg
29017     },
29018     
29019     initEvents: function(ct, position)
29020     {       
29021         if(!this.el.getWidth() || this.isApplied()){
29022             return;
29023         }
29024         
29025         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29026         
29027         this.initial();
29028     },
29029     
29030     initial: function()
29031     {
29032         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29033             this.fireEvent('loadexception', this);
29034             return;
29035         }
29036         
29037         if(!this.mapTypeId){
29038             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29039         }
29040         
29041         this.gMapContext = this.GMapContext();
29042         
29043         this.initOverlayView();
29044         
29045         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29046         
29047         var _this = this;
29048                 
29049         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29050             _this.setPosition(_this.gMapContext.marker.position);
29051         });
29052         
29053         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29054             _this.fireEvent('mapClick', this, event);
29055             
29056         });
29057
29058         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29059             _this.fireEvent('mapRightClick', this, event);
29060             
29061         });
29062         
29063         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29064             _this.fireEvent('markerClick', this, event);
29065             
29066         });
29067
29068         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29069             _this.fireEvent('markerRightClick', this, event);
29070             
29071         });
29072         
29073         this.setPosition(this.gMapContext.location);
29074         
29075         this.fireEvent('initial', this, this.gMapContext.location);
29076     },
29077     
29078     initOverlayView: function()
29079     {
29080         var _this = this;
29081         
29082         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29083             
29084             draw: function()
29085             {
29086                 _this.fireEvent('OverlayViewDraw', _this);
29087             },
29088             
29089             onAdd: function()
29090             {
29091                 _this.fireEvent('OverlayViewOnAdd', _this);
29092             },
29093             
29094             onRemove: function()
29095             {
29096                 _this.fireEvent('OverlayViewOnRemove', _this);
29097             },
29098             
29099             show: function(cpx)
29100             {
29101                 _this.fireEvent('OverlayViewShow', _this, cpx);
29102             },
29103             
29104             hide: function()
29105             {
29106                 _this.fireEvent('OverlayViewHide', _this);
29107             }
29108             
29109         });
29110     },
29111     
29112     fromLatLngToContainerPixel: function(event)
29113     {
29114         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29115     },
29116     
29117     isApplied: function() 
29118     {
29119         return this.getGmapContext() == false ? false : true;
29120     },
29121     
29122     getGmapContext: function() 
29123     {
29124         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29125     },
29126     
29127     GMapContext: function() 
29128     {
29129         var position = new google.maps.LatLng(this.latitude, this.longitude);
29130         
29131         var _map = new google.maps.Map(this.el.dom, {
29132             center: position,
29133             zoom: this.zoom,
29134             mapTypeId: this.mapTypeId,
29135             mapTypeControl: this.mapTypeControl,
29136             disableDoubleClickZoom: this.disableDoubleClickZoom,
29137             scrollwheel: this.scrollwheel,
29138             streetViewControl: this.streetViewControl,
29139             locationName: this.locationName,
29140             draggable: this.draggable,
29141             enableAutocomplete: this.enableAutocomplete,
29142             enableReverseGeocode: this.enableReverseGeocode
29143         });
29144         
29145         var _marker = new google.maps.Marker({
29146             position: position,
29147             map: _map,
29148             title: this.markerTitle,
29149             draggable: this.draggable
29150         });
29151         
29152         return {
29153             map: _map,
29154             marker: _marker,
29155             circle: null,
29156             location: position,
29157             radius: this.radius,
29158             locationName: this.locationName,
29159             addressComponents: {
29160                 formatted_address: null,
29161                 addressLine1: null,
29162                 addressLine2: null,
29163                 streetName: null,
29164                 streetNumber: null,
29165                 city: null,
29166                 district: null,
29167                 state: null,
29168                 stateOrProvince: null
29169             },
29170             settings: this,
29171             domContainer: this.el.dom,
29172             geodecoder: new google.maps.Geocoder()
29173         };
29174     },
29175     
29176     drawCircle: function(center, radius, options) 
29177     {
29178         if (this.gMapContext.circle != null) {
29179             this.gMapContext.circle.setMap(null);
29180         }
29181         if (radius > 0) {
29182             radius *= 1;
29183             options = Roo.apply({}, options, {
29184                 strokeColor: "#0000FF",
29185                 strokeOpacity: .35,
29186                 strokeWeight: 2,
29187                 fillColor: "#0000FF",
29188                 fillOpacity: .2
29189             });
29190             
29191             options.map = this.gMapContext.map;
29192             options.radius = radius;
29193             options.center = center;
29194             this.gMapContext.circle = new google.maps.Circle(options);
29195             return this.gMapContext.circle;
29196         }
29197         
29198         return null;
29199     },
29200     
29201     setPosition: function(location) 
29202     {
29203         this.gMapContext.location = location;
29204         this.gMapContext.marker.setPosition(location);
29205         this.gMapContext.map.panTo(location);
29206         this.drawCircle(location, this.gMapContext.radius, {});
29207         
29208         var _this = this;
29209         
29210         if (this.gMapContext.settings.enableReverseGeocode) {
29211             this.gMapContext.geodecoder.geocode({
29212                 latLng: this.gMapContext.location
29213             }, function(results, status) {
29214                 
29215                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29216                     _this.gMapContext.locationName = results[0].formatted_address;
29217                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29218                     
29219                     _this.fireEvent('positionchanged', this, location);
29220                 }
29221             });
29222             
29223             return;
29224         }
29225         
29226         this.fireEvent('positionchanged', this, location);
29227     },
29228     
29229     resize: function()
29230     {
29231         google.maps.event.trigger(this.gMapContext.map, "resize");
29232         
29233         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29234         
29235         this.fireEvent('resize', this);
29236     },
29237     
29238     setPositionByLatLng: function(latitude, longitude)
29239     {
29240         this.setPosition(new google.maps.LatLng(latitude, longitude));
29241     },
29242     
29243     getCurrentPosition: function() 
29244     {
29245         return {
29246             latitude: this.gMapContext.location.lat(),
29247             longitude: this.gMapContext.location.lng()
29248         };
29249     },
29250     
29251     getAddressName: function() 
29252     {
29253         return this.gMapContext.locationName;
29254     },
29255     
29256     getAddressComponents: function() 
29257     {
29258         return this.gMapContext.addressComponents;
29259     },
29260     
29261     address_component_from_google_geocode: function(address_components) 
29262     {
29263         var result = {};
29264         
29265         for (var i = 0; i < address_components.length; i++) {
29266             var component = address_components[i];
29267             if (component.types.indexOf("postal_code") >= 0) {
29268                 result.postalCode = component.short_name;
29269             } else if (component.types.indexOf("street_number") >= 0) {
29270                 result.streetNumber = component.short_name;
29271             } else if (component.types.indexOf("route") >= 0) {
29272                 result.streetName = component.short_name;
29273             } else if (component.types.indexOf("neighborhood") >= 0) {
29274                 result.city = component.short_name;
29275             } else if (component.types.indexOf("locality") >= 0) {
29276                 result.city = component.short_name;
29277             } else if (component.types.indexOf("sublocality") >= 0) {
29278                 result.district = component.short_name;
29279             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29280                 result.stateOrProvince = component.short_name;
29281             } else if (component.types.indexOf("country") >= 0) {
29282                 result.country = component.short_name;
29283             }
29284         }
29285         
29286         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29287         result.addressLine2 = "";
29288         return result;
29289     },
29290     
29291     setZoomLevel: function(zoom)
29292     {
29293         this.gMapContext.map.setZoom(zoom);
29294     },
29295     
29296     show: function()
29297     {
29298         if(!this.el){
29299             return;
29300         }
29301         
29302         this.el.show();
29303         
29304         this.resize();
29305         
29306         this.fireEvent('show', this);
29307     },
29308     
29309     hide: function()
29310     {
29311         if(!this.el){
29312             return;
29313         }
29314         
29315         this.el.hide();
29316         
29317         this.fireEvent('hide', this);
29318     }
29319     
29320 });
29321
29322 Roo.apply(Roo.bootstrap.LocationPicker, {
29323     
29324     OverlayView : function(map, options)
29325     {
29326         options = options || {};
29327         
29328         this.setMap(map);
29329     }
29330     
29331     
29332 });/**
29333  * @class Roo.bootstrap.Alert
29334  * @extends Roo.bootstrap.Component
29335  * Bootstrap Alert class - shows an alert area box
29336  * eg
29337  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29338   Enter a valid email address
29339 </div>
29340  * @licence LGPL
29341  * @cfg {String} title The title of alert
29342  * @cfg {String} html The content of alert
29343  * @cfg {String} weight (  success | info | warning | danger )
29344  * @cfg {String} faicon font-awesomeicon
29345  * 
29346  * @constructor
29347  * Create a new alert
29348  * @param {Object} config The config object
29349  */
29350
29351
29352 Roo.bootstrap.Alert = function(config){
29353     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29354     
29355 };
29356
29357 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29358     
29359     title: '',
29360     html: '',
29361     weight: false,
29362     faicon: false,
29363     
29364     getAutoCreate : function()
29365     {
29366         
29367         var cfg = {
29368             tag : 'div',
29369             cls : 'alert',
29370             cn : [
29371                 {
29372                     tag : 'i',
29373                     cls : 'roo-alert-icon'
29374                     
29375                 },
29376                 {
29377                     tag : 'b',
29378                     cls : 'roo-alert-title',
29379                     html : this.title
29380                 },
29381                 {
29382                     tag : 'span',
29383                     cls : 'roo-alert-text',
29384                     html : this.html
29385                 }
29386             ]
29387         };
29388         
29389         if(this.faicon){
29390             cfg.cn[0].cls += ' fa ' + this.faicon;
29391         }
29392         
29393         if(this.weight){
29394             cfg.cls += ' alert-' + this.weight;
29395         }
29396         
29397         return cfg;
29398     },
29399     
29400     initEvents: function() 
29401     {
29402         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29403     },
29404     
29405     setTitle : function(str)
29406     {
29407         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29408     },
29409     
29410     setText : function(str)
29411     {
29412         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29413     },
29414     
29415     setWeight : function(weight)
29416     {
29417         if(this.weight){
29418             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29419         }
29420         
29421         this.weight = weight;
29422         
29423         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29424     },
29425     
29426     setIcon : function(icon)
29427     {
29428         if(this.faicon){
29429             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29430         }
29431         
29432         this.faicon = icon;
29433         
29434         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29435     },
29436     
29437     hide: function() 
29438     {
29439         this.el.hide();   
29440     },
29441     
29442     show: function() 
29443     {  
29444         this.el.show();   
29445     }
29446     
29447 });
29448
29449  
29450 /*
29451 * Licence: LGPL
29452 */
29453
29454 /**
29455  * @class Roo.bootstrap.UploadCropbox
29456  * @extends Roo.bootstrap.Component
29457  * Bootstrap UploadCropbox class
29458  * @cfg {String} emptyText show when image has been loaded
29459  * @cfg {String} rotateNotify show when image too small to rotate
29460  * @cfg {Number} errorTimeout default 3000
29461  * @cfg {Number} minWidth default 300
29462  * @cfg {Number} minHeight default 300
29463  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29464  * @cfg {Boolean} isDocument (true|false) default false
29465  * @cfg {String} url action url
29466  * @cfg {String} paramName default 'imageUpload'
29467  * @cfg {String} method default POST
29468  * @cfg {Boolean} loadMask (true|false) default true
29469  * @cfg {Boolean} loadingText default 'Loading...'
29470  * 
29471  * @constructor
29472  * Create a new UploadCropbox
29473  * @param {Object} config The config object
29474  */
29475
29476 Roo.bootstrap.UploadCropbox = function(config){
29477     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29478     
29479     this.addEvents({
29480         /**
29481          * @event beforeselectfile
29482          * Fire before select file
29483          * @param {Roo.bootstrap.UploadCropbox} this
29484          */
29485         "beforeselectfile" : true,
29486         /**
29487          * @event initial
29488          * Fire after initEvent
29489          * @param {Roo.bootstrap.UploadCropbox} this
29490          */
29491         "initial" : true,
29492         /**
29493          * @event crop
29494          * Fire after initEvent
29495          * @param {Roo.bootstrap.UploadCropbox} this
29496          * @param {String} data
29497          */
29498         "crop" : true,
29499         /**
29500          * @event prepare
29501          * Fire when preparing the file data
29502          * @param {Roo.bootstrap.UploadCropbox} this
29503          * @param {Object} file
29504          */
29505         "prepare" : true,
29506         /**
29507          * @event exception
29508          * Fire when get exception
29509          * @param {Roo.bootstrap.UploadCropbox} this
29510          * @param {XMLHttpRequest} xhr
29511          */
29512         "exception" : true,
29513         /**
29514          * @event beforeloadcanvas
29515          * Fire before load the canvas
29516          * @param {Roo.bootstrap.UploadCropbox} this
29517          * @param {String} src
29518          */
29519         "beforeloadcanvas" : true,
29520         /**
29521          * @event trash
29522          * Fire when trash image
29523          * @param {Roo.bootstrap.UploadCropbox} this
29524          */
29525         "trash" : true,
29526         /**
29527          * @event download
29528          * Fire when download the image
29529          * @param {Roo.bootstrap.UploadCropbox} this
29530          */
29531         "download" : true,
29532         /**
29533          * @event footerbuttonclick
29534          * Fire when footerbuttonclick
29535          * @param {Roo.bootstrap.UploadCropbox} this
29536          * @param {String} type
29537          */
29538         "footerbuttonclick" : true,
29539         /**
29540          * @event resize
29541          * Fire when resize
29542          * @param {Roo.bootstrap.UploadCropbox} this
29543          */
29544         "resize" : true,
29545         /**
29546          * @event rotate
29547          * Fire when rotate the image
29548          * @param {Roo.bootstrap.UploadCropbox} this
29549          * @param {String} pos
29550          */
29551         "rotate" : true,
29552         /**
29553          * @event inspect
29554          * Fire when inspect the file
29555          * @param {Roo.bootstrap.UploadCropbox} this
29556          * @param {Object} file
29557          */
29558         "inspect" : true,
29559         /**
29560          * @event upload
29561          * Fire when xhr upload the file
29562          * @param {Roo.bootstrap.UploadCropbox} this
29563          * @param {Object} data
29564          */
29565         "upload" : true,
29566         /**
29567          * @event arrange
29568          * Fire when arrange the file data
29569          * @param {Roo.bootstrap.UploadCropbox} this
29570          * @param {Object} formData
29571          */
29572         "arrange" : true
29573     });
29574     
29575     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29576 };
29577
29578 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29579     
29580     emptyText : 'Click to upload image',
29581     rotateNotify : 'Image is too small to rotate',
29582     errorTimeout : 3000,
29583     scale : 0,
29584     baseScale : 1,
29585     rotate : 0,
29586     dragable : false,
29587     pinching : false,
29588     mouseX : 0,
29589     mouseY : 0,
29590     cropData : false,
29591     minWidth : 300,
29592     minHeight : 300,
29593     file : false,
29594     exif : {},
29595     baseRotate : 1,
29596     cropType : 'image/jpeg',
29597     buttons : false,
29598     canvasLoaded : false,
29599     isDocument : false,
29600     method : 'POST',
29601     paramName : 'imageUpload',
29602     loadMask : true,
29603     loadingText : 'Loading...',
29604     maskEl : false,
29605     
29606     getAutoCreate : function()
29607     {
29608         var cfg = {
29609             tag : 'div',
29610             cls : 'roo-upload-cropbox',
29611             cn : [
29612                 {
29613                     tag : 'input',
29614                     cls : 'roo-upload-cropbox-selector',
29615                     type : 'file'
29616                 },
29617                 {
29618                     tag : 'div',
29619                     cls : 'roo-upload-cropbox-body',
29620                     style : 'cursor:pointer',
29621                     cn : [
29622                         {
29623                             tag : 'div',
29624                             cls : 'roo-upload-cropbox-preview'
29625                         },
29626                         {
29627                             tag : 'div',
29628                             cls : 'roo-upload-cropbox-thumb'
29629                         },
29630                         {
29631                             tag : 'div',
29632                             cls : 'roo-upload-cropbox-empty-notify',
29633                             html : this.emptyText
29634                         },
29635                         {
29636                             tag : 'div',
29637                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29638                             html : this.rotateNotify
29639                         }
29640                     ]
29641                 },
29642                 {
29643                     tag : 'div',
29644                     cls : 'roo-upload-cropbox-footer',
29645                     cn : {
29646                         tag : 'div',
29647                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29648                         cn : []
29649                     }
29650                 }
29651             ]
29652         };
29653         
29654         return cfg;
29655     },
29656     
29657     onRender : function(ct, position)
29658     {
29659         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29660         
29661         if (this.buttons.length) {
29662             
29663             Roo.each(this.buttons, function(bb) {
29664                 
29665                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29666                 
29667                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29668                 
29669             }, this);
29670         }
29671         
29672         if(this.loadMask){
29673             this.maskEl = this.el;
29674         }
29675     },
29676     
29677     initEvents : function()
29678     {
29679         this.urlAPI = (window.createObjectURL && window) || 
29680                                 (window.URL && URL.revokeObjectURL && URL) || 
29681                                 (window.webkitURL && webkitURL);
29682                         
29683         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29684         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29685         
29686         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29687         this.selectorEl.hide();
29688         
29689         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29690         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29691         
29692         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29693         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29694         this.thumbEl.hide();
29695         
29696         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29697         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29698         
29699         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29700         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29701         this.errorEl.hide();
29702         
29703         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29704         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29705         this.footerEl.hide();
29706         
29707         this.setThumbBoxSize();
29708         
29709         this.bind();
29710         
29711         this.resize();
29712         
29713         this.fireEvent('initial', this);
29714     },
29715
29716     bind : function()
29717     {
29718         var _this = this;
29719         
29720         window.addEventListener("resize", function() { _this.resize(); } );
29721         
29722         this.bodyEl.on('click', this.beforeSelectFile, this);
29723         
29724         if(Roo.isTouch){
29725             this.bodyEl.on('touchstart', this.onTouchStart, this);
29726             this.bodyEl.on('touchmove', this.onTouchMove, this);
29727             this.bodyEl.on('touchend', this.onTouchEnd, this);
29728         }
29729         
29730         if(!Roo.isTouch){
29731             this.bodyEl.on('mousedown', this.onMouseDown, this);
29732             this.bodyEl.on('mousemove', this.onMouseMove, this);
29733             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29734             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29735             Roo.get(document).on('mouseup', this.onMouseUp, this);
29736         }
29737         
29738         this.selectorEl.on('change', this.onFileSelected, this);
29739     },
29740     
29741     reset : function()
29742     {    
29743         this.scale = 0;
29744         this.baseScale = 1;
29745         this.rotate = 0;
29746         this.baseRotate = 1;
29747         this.dragable = false;
29748         this.pinching = false;
29749         this.mouseX = 0;
29750         this.mouseY = 0;
29751         this.cropData = false;
29752         this.notifyEl.dom.innerHTML = this.emptyText;
29753         
29754         this.selectorEl.dom.value = '';
29755         
29756     },
29757     
29758     resize : function()
29759     {
29760         if(this.fireEvent('resize', this) != false){
29761             this.setThumbBoxPosition();
29762             this.setCanvasPosition();
29763         }
29764     },
29765     
29766     onFooterButtonClick : function(e, el, o, type)
29767     {
29768         switch (type) {
29769             case 'rotate-left' :
29770                 this.onRotateLeft(e);
29771                 break;
29772             case 'rotate-right' :
29773                 this.onRotateRight(e);
29774                 break;
29775             case 'picture' :
29776                 this.beforeSelectFile(e);
29777                 break;
29778             case 'trash' :
29779                 this.trash(e);
29780                 break;
29781             case 'crop' :
29782                 this.crop(e);
29783                 break;
29784             case 'download' :
29785                 this.download(e);
29786                 break;
29787             default :
29788                 break;
29789         }
29790         
29791         this.fireEvent('footerbuttonclick', this, type);
29792     },
29793     
29794     beforeSelectFile : function(e)
29795     {
29796         e.preventDefault();
29797         
29798         if(this.fireEvent('beforeselectfile', this) != false){
29799             this.selectorEl.dom.click();
29800         }
29801     },
29802     
29803     onFileSelected : function(e)
29804     {
29805         e.preventDefault();
29806         
29807         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29808             return;
29809         }
29810         
29811         var file = this.selectorEl.dom.files[0];
29812         
29813         if(this.fireEvent('inspect', this, file) != false){
29814             this.prepare(file);
29815         }
29816         
29817     },
29818     
29819     trash : function(e)
29820     {
29821         this.fireEvent('trash', this);
29822     },
29823     
29824     download : function(e)
29825     {
29826         this.fireEvent('download', this);
29827     },
29828     
29829     loadCanvas : function(src)
29830     {   
29831         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29832             
29833             this.reset();
29834             
29835             this.imageEl = document.createElement('img');
29836             
29837             var _this = this;
29838             
29839             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29840             
29841             this.imageEl.src = src;
29842         }
29843     },
29844     
29845     onLoadCanvas : function()
29846     {   
29847         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29848         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29849         
29850         this.bodyEl.un('click', this.beforeSelectFile, this);
29851         
29852         this.notifyEl.hide();
29853         this.thumbEl.show();
29854         this.footerEl.show();
29855         
29856         this.baseRotateLevel();
29857         
29858         if(this.isDocument){
29859             this.setThumbBoxSize();
29860         }
29861         
29862         this.setThumbBoxPosition();
29863         
29864         this.baseScaleLevel();
29865         
29866         this.draw();
29867         
29868         this.resize();
29869         
29870         this.canvasLoaded = true;
29871         
29872         if(this.loadMask){
29873             this.maskEl.unmask();
29874         }
29875         
29876     },
29877     
29878     setCanvasPosition : function()
29879     {   
29880         if(!this.canvasEl){
29881             return;
29882         }
29883         
29884         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29885         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29886         
29887         this.previewEl.setLeft(pw);
29888         this.previewEl.setTop(ph);
29889         
29890     },
29891     
29892     onMouseDown : function(e)
29893     {   
29894         e.stopEvent();
29895         
29896         this.dragable = true;
29897         this.pinching = false;
29898         
29899         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29900             this.dragable = false;
29901             return;
29902         }
29903         
29904         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29905         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29906         
29907     },
29908     
29909     onMouseMove : function(e)
29910     {   
29911         e.stopEvent();
29912         
29913         if(!this.canvasLoaded){
29914             return;
29915         }
29916         
29917         if (!this.dragable){
29918             return;
29919         }
29920         
29921         var minX = Math.ceil(this.thumbEl.getLeft(true));
29922         var minY = Math.ceil(this.thumbEl.getTop(true));
29923         
29924         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29925         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29926         
29927         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29928         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29929         
29930         x = x - this.mouseX;
29931         y = y - this.mouseY;
29932         
29933         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29934         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29935         
29936         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29937         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29938         
29939         this.previewEl.setLeft(bgX);
29940         this.previewEl.setTop(bgY);
29941         
29942         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29943         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29944     },
29945     
29946     onMouseUp : function(e)
29947     {   
29948         e.stopEvent();
29949         
29950         this.dragable = false;
29951     },
29952     
29953     onMouseWheel : function(e)
29954     {   
29955         e.stopEvent();
29956         
29957         this.startScale = this.scale;
29958         
29959         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29960         
29961         if(!this.zoomable()){
29962             this.scale = this.startScale;
29963             return;
29964         }
29965         
29966         this.draw();
29967         
29968         return;
29969     },
29970     
29971     zoomable : function()
29972     {
29973         var minScale = this.thumbEl.getWidth() / this.minWidth;
29974         
29975         if(this.minWidth < this.minHeight){
29976             minScale = this.thumbEl.getHeight() / this.minHeight;
29977         }
29978         
29979         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29980         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29981         
29982         if(
29983                 this.isDocument &&
29984                 (this.rotate == 0 || this.rotate == 180) && 
29985                 (
29986                     width > this.imageEl.OriginWidth || 
29987                     height > this.imageEl.OriginHeight ||
29988                     (width < this.minWidth && height < this.minHeight)
29989                 )
29990         ){
29991             return false;
29992         }
29993         
29994         if(
29995                 this.isDocument &&
29996                 (this.rotate == 90 || this.rotate == 270) && 
29997                 (
29998                     width > this.imageEl.OriginWidth || 
29999                     height > this.imageEl.OriginHeight ||
30000                     (width < this.minHeight && height < this.minWidth)
30001                 )
30002         ){
30003             return false;
30004         }
30005         
30006         if(
30007                 !this.isDocument &&
30008                 (this.rotate == 0 || this.rotate == 180) && 
30009                 (
30010                     width < this.minWidth || 
30011                     width > this.imageEl.OriginWidth || 
30012                     height < this.minHeight || 
30013                     height > this.imageEl.OriginHeight
30014                 )
30015         ){
30016             return false;
30017         }
30018         
30019         if(
30020                 !this.isDocument &&
30021                 (this.rotate == 90 || this.rotate == 270) && 
30022                 (
30023                     width < this.minHeight || 
30024                     width > this.imageEl.OriginWidth || 
30025                     height < this.minWidth || 
30026                     height > this.imageEl.OriginHeight
30027                 )
30028         ){
30029             return false;
30030         }
30031         
30032         return true;
30033         
30034     },
30035     
30036     onRotateLeft : function(e)
30037     {   
30038         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30039             
30040             var minScale = this.thumbEl.getWidth() / this.minWidth;
30041             
30042             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30043             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30044             
30045             this.startScale = this.scale;
30046             
30047             while (this.getScaleLevel() < minScale){
30048             
30049                 this.scale = this.scale + 1;
30050                 
30051                 if(!this.zoomable()){
30052                     break;
30053                 }
30054                 
30055                 if(
30056                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30057                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30058                 ){
30059                     continue;
30060                 }
30061                 
30062                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30063
30064                 this.draw();
30065                 
30066                 return;
30067             }
30068             
30069             this.scale = this.startScale;
30070             
30071             this.onRotateFail();
30072             
30073             return false;
30074         }
30075         
30076         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30077
30078         if(this.isDocument){
30079             this.setThumbBoxSize();
30080             this.setThumbBoxPosition();
30081             this.setCanvasPosition();
30082         }
30083         
30084         this.draw();
30085         
30086         this.fireEvent('rotate', this, 'left');
30087         
30088     },
30089     
30090     onRotateRight : function(e)
30091     {
30092         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30093             
30094             var minScale = this.thumbEl.getWidth() / this.minWidth;
30095         
30096             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30097             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30098             
30099             this.startScale = this.scale;
30100             
30101             while (this.getScaleLevel() < minScale){
30102             
30103                 this.scale = this.scale + 1;
30104                 
30105                 if(!this.zoomable()){
30106                     break;
30107                 }
30108                 
30109                 if(
30110                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30111                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30112                 ){
30113                     continue;
30114                 }
30115                 
30116                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30117
30118                 this.draw();
30119                 
30120                 return;
30121             }
30122             
30123             this.scale = this.startScale;
30124             
30125             this.onRotateFail();
30126             
30127             return false;
30128         }
30129         
30130         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30131
30132         if(this.isDocument){
30133             this.setThumbBoxSize();
30134             this.setThumbBoxPosition();
30135             this.setCanvasPosition();
30136         }
30137         
30138         this.draw();
30139         
30140         this.fireEvent('rotate', this, 'right');
30141     },
30142     
30143     onRotateFail : function()
30144     {
30145         this.errorEl.show(true);
30146         
30147         var _this = this;
30148         
30149         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30150     },
30151     
30152     draw : function()
30153     {
30154         this.previewEl.dom.innerHTML = '';
30155         
30156         var canvasEl = document.createElement("canvas");
30157         
30158         var contextEl = canvasEl.getContext("2d");
30159         
30160         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30161         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30162         var center = this.imageEl.OriginWidth / 2;
30163         
30164         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30165             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30166             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30167             center = this.imageEl.OriginHeight / 2;
30168         }
30169         
30170         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30171         
30172         contextEl.translate(center, center);
30173         contextEl.rotate(this.rotate * Math.PI / 180);
30174
30175         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30176         
30177         this.canvasEl = document.createElement("canvas");
30178         
30179         this.contextEl = this.canvasEl.getContext("2d");
30180         
30181         switch (this.rotate) {
30182             case 0 :
30183                 
30184                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30185                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30186                 
30187                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30188                 
30189                 break;
30190             case 90 : 
30191                 
30192                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30193                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30194                 
30195                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30196                     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);
30197                     break;
30198                 }
30199                 
30200                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30201                 
30202                 break;
30203             case 180 :
30204                 
30205                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30206                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30207                 
30208                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30209                     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);
30210                     break;
30211                 }
30212                 
30213                 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);
30214                 
30215                 break;
30216             case 270 :
30217                 
30218                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30219                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30220         
30221                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30222                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30223                     break;
30224                 }
30225                 
30226                 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);
30227                 
30228                 break;
30229             default : 
30230                 break;
30231         }
30232         
30233         this.previewEl.appendChild(this.canvasEl);
30234         
30235         this.setCanvasPosition();
30236     },
30237     
30238     crop : function()
30239     {
30240         if(!this.canvasLoaded){
30241             return;
30242         }
30243         
30244         var imageCanvas = document.createElement("canvas");
30245         
30246         var imageContext = imageCanvas.getContext("2d");
30247         
30248         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30249         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30250         
30251         var center = imageCanvas.width / 2;
30252         
30253         imageContext.translate(center, center);
30254         
30255         imageContext.rotate(this.rotate * Math.PI / 180);
30256         
30257         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30258         
30259         var canvas = document.createElement("canvas");
30260         
30261         var context = canvas.getContext("2d");
30262                 
30263         canvas.width = this.minWidth;
30264         canvas.height = this.minHeight;
30265
30266         switch (this.rotate) {
30267             case 0 :
30268                 
30269                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30270                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30271                 
30272                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30273                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30274                 
30275                 var targetWidth = this.minWidth - 2 * x;
30276                 var targetHeight = this.minHeight - 2 * y;
30277                 
30278                 var scale = 1;
30279                 
30280                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30281                     scale = targetWidth / width;
30282                 }
30283                 
30284                 if(x > 0 && y == 0){
30285                     scale = targetHeight / height;
30286                 }
30287                 
30288                 if(x > 0 && y > 0){
30289                     scale = targetWidth / width;
30290                     
30291                     if(width < height){
30292                         scale = targetHeight / height;
30293                     }
30294                 }
30295                 
30296                 context.scale(scale, scale);
30297                 
30298                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30299                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30300
30301                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30302                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30303
30304                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30305                 
30306                 break;
30307             case 90 : 
30308                 
30309                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30310                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30311                 
30312                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30313                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30314                 
30315                 var targetWidth = this.minWidth - 2 * x;
30316                 var targetHeight = this.minHeight - 2 * y;
30317                 
30318                 var scale = 1;
30319                 
30320                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30321                     scale = targetWidth / width;
30322                 }
30323                 
30324                 if(x > 0 && y == 0){
30325                     scale = targetHeight / height;
30326                 }
30327                 
30328                 if(x > 0 && y > 0){
30329                     scale = targetWidth / width;
30330                     
30331                     if(width < height){
30332                         scale = targetHeight / height;
30333                     }
30334                 }
30335                 
30336                 context.scale(scale, scale);
30337                 
30338                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30339                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30340
30341                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30342                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30343                 
30344                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30345                 
30346                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30347                 
30348                 break;
30349             case 180 :
30350                 
30351                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30352                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30353                 
30354                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30355                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30356                 
30357                 var targetWidth = this.minWidth - 2 * x;
30358                 var targetHeight = this.minHeight - 2 * y;
30359                 
30360                 var scale = 1;
30361                 
30362                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30363                     scale = targetWidth / width;
30364                 }
30365                 
30366                 if(x > 0 && y == 0){
30367                     scale = targetHeight / height;
30368                 }
30369                 
30370                 if(x > 0 && y > 0){
30371                     scale = targetWidth / width;
30372                     
30373                     if(width < height){
30374                         scale = targetHeight / height;
30375                     }
30376                 }
30377                 
30378                 context.scale(scale, scale);
30379                 
30380                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30381                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30382
30383                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30384                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30385
30386                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30387                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30388                 
30389                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30390                 
30391                 break;
30392             case 270 :
30393                 
30394                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30395                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30396                 
30397                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30398                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30399                 
30400                 var targetWidth = this.minWidth - 2 * x;
30401                 var targetHeight = this.minHeight - 2 * y;
30402                 
30403                 var scale = 1;
30404                 
30405                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30406                     scale = targetWidth / width;
30407                 }
30408                 
30409                 if(x > 0 && y == 0){
30410                     scale = targetHeight / height;
30411                 }
30412                 
30413                 if(x > 0 && y > 0){
30414                     scale = targetWidth / width;
30415                     
30416                     if(width < height){
30417                         scale = targetHeight / height;
30418                     }
30419                 }
30420                 
30421                 context.scale(scale, scale);
30422                 
30423                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30424                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30425
30426                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30427                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30428                 
30429                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30430                 
30431                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30432                 
30433                 break;
30434             default : 
30435                 break;
30436         }
30437         
30438         this.cropData = canvas.toDataURL(this.cropType);
30439         
30440         if(this.fireEvent('crop', this, this.cropData) !== false){
30441             this.process(this.file, this.cropData);
30442         }
30443         
30444         return;
30445         
30446     },
30447     
30448     setThumbBoxSize : function()
30449     {
30450         var width, height;
30451         
30452         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30453             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30454             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30455             
30456             this.minWidth = width;
30457             this.minHeight = height;
30458             
30459             if(this.rotate == 90 || this.rotate == 270){
30460                 this.minWidth = height;
30461                 this.minHeight = width;
30462             }
30463         }
30464         
30465         height = 300;
30466         width = Math.ceil(this.minWidth * height / this.minHeight);
30467         
30468         if(this.minWidth > this.minHeight){
30469             width = 300;
30470             height = Math.ceil(this.minHeight * width / this.minWidth);
30471         }
30472         
30473         this.thumbEl.setStyle({
30474             width : width + 'px',
30475             height : height + 'px'
30476         });
30477
30478         return;
30479             
30480     },
30481     
30482     setThumbBoxPosition : function()
30483     {
30484         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30485         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30486         
30487         this.thumbEl.setLeft(x);
30488         this.thumbEl.setTop(y);
30489         
30490     },
30491     
30492     baseRotateLevel : function()
30493     {
30494         this.baseRotate = 1;
30495         
30496         if(
30497                 typeof(this.exif) != 'undefined' &&
30498                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30499                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30500         ){
30501             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30502         }
30503         
30504         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30505         
30506     },
30507     
30508     baseScaleLevel : function()
30509     {
30510         var width, height;
30511         
30512         if(this.isDocument){
30513             
30514             if(this.baseRotate == 6 || this.baseRotate == 8){
30515             
30516                 height = this.thumbEl.getHeight();
30517                 this.baseScale = height / this.imageEl.OriginWidth;
30518
30519                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30520                     width = this.thumbEl.getWidth();
30521                     this.baseScale = width / this.imageEl.OriginHeight;
30522                 }
30523
30524                 return;
30525             }
30526
30527             height = this.thumbEl.getHeight();
30528             this.baseScale = height / this.imageEl.OriginHeight;
30529
30530             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30531                 width = this.thumbEl.getWidth();
30532                 this.baseScale = width / this.imageEl.OriginWidth;
30533             }
30534
30535             return;
30536         }
30537         
30538         if(this.baseRotate == 6 || this.baseRotate == 8){
30539             
30540             width = this.thumbEl.getHeight();
30541             this.baseScale = width / this.imageEl.OriginHeight;
30542             
30543             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30544                 height = this.thumbEl.getWidth();
30545                 this.baseScale = height / this.imageEl.OriginHeight;
30546             }
30547             
30548             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30549                 height = this.thumbEl.getWidth();
30550                 this.baseScale = height / this.imageEl.OriginHeight;
30551                 
30552                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30553                     width = this.thumbEl.getHeight();
30554                     this.baseScale = width / this.imageEl.OriginWidth;
30555                 }
30556             }
30557             
30558             return;
30559         }
30560         
30561         width = this.thumbEl.getWidth();
30562         this.baseScale = width / this.imageEl.OriginWidth;
30563         
30564         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30565             height = this.thumbEl.getHeight();
30566             this.baseScale = height / this.imageEl.OriginHeight;
30567         }
30568         
30569         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30570             
30571             height = this.thumbEl.getHeight();
30572             this.baseScale = height / this.imageEl.OriginHeight;
30573             
30574             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30575                 width = this.thumbEl.getWidth();
30576                 this.baseScale = width / this.imageEl.OriginWidth;
30577             }
30578             
30579         }
30580         
30581         return;
30582     },
30583     
30584     getScaleLevel : function()
30585     {
30586         return this.baseScale * Math.pow(1.1, this.scale);
30587     },
30588     
30589     onTouchStart : function(e)
30590     {
30591         if(!this.canvasLoaded){
30592             this.beforeSelectFile(e);
30593             return;
30594         }
30595         
30596         var touches = e.browserEvent.touches;
30597         
30598         if(!touches){
30599             return;
30600         }
30601         
30602         if(touches.length == 1){
30603             this.onMouseDown(e);
30604             return;
30605         }
30606         
30607         if(touches.length != 2){
30608             return;
30609         }
30610         
30611         var coords = [];
30612         
30613         for(var i = 0, finger; finger = touches[i]; i++){
30614             coords.push(finger.pageX, finger.pageY);
30615         }
30616         
30617         var x = Math.pow(coords[0] - coords[2], 2);
30618         var y = Math.pow(coords[1] - coords[3], 2);
30619         
30620         this.startDistance = Math.sqrt(x + y);
30621         
30622         this.startScale = this.scale;
30623         
30624         this.pinching = true;
30625         this.dragable = false;
30626         
30627     },
30628     
30629     onTouchMove : function(e)
30630     {
30631         if(!this.pinching && !this.dragable){
30632             return;
30633         }
30634         
30635         var touches = e.browserEvent.touches;
30636         
30637         if(!touches){
30638             return;
30639         }
30640         
30641         if(this.dragable){
30642             this.onMouseMove(e);
30643             return;
30644         }
30645         
30646         var coords = [];
30647         
30648         for(var i = 0, finger; finger = touches[i]; i++){
30649             coords.push(finger.pageX, finger.pageY);
30650         }
30651         
30652         var x = Math.pow(coords[0] - coords[2], 2);
30653         var y = Math.pow(coords[1] - coords[3], 2);
30654         
30655         this.endDistance = Math.sqrt(x + y);
30656         
30657         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30658         
30659         if(!this.zoomable()){
30660             this.scale = this.startScale;
30661             return;
30662         }
30663         
30664         this.draw();
30665         
30666     },
30667     
30668     onTouchEnd : function(e)
30669     {
30670         this.pinching = false;
30671         this.dragable = false;
30672         
30673     },
30674     
30675     process : function(file, crop)
30676     {
30677         if(this.loadMask){
30678             this.maskEl.mask(this.loadingText);
30679         }
30680         
30681         this.xhr = new XMLHttpRequest();
30682         
30683         file.xhr = this.xhr;
30684
30685         this.xhr.open(this.method, this.url, true);
30686         
30687         var headers = {
30688             "Accept": "application/json",
30689             "Cache-Control": "no-cache",
30690             "X-Requested-With": "XMLHttpRequest"
30691         };
30692         
30693         for (var headerName in headers) {
30694             var headerValue = headers[headerName];
30695             if (headerValue) {
30696                 this.xhr.setRequestHeader(headerName, headerValue);
30697             }
30698         }
30699         
30700         var _this = this;
30701         
30702         this.xhr.onload = function()
30703         {
30704             _this.xhrOnLoad(_this.xhr);
30705         }
30706         
30707         this.xhr.onerror = function()
30708         {
30709             _this.xhrOnError(_this.xhr);
30710         }
30711         
30712         var formData = new FormData();
30713
30714         formData.append('returnHTML', 'NO');
30715         
30716         if(crop){
30717             formData.append('crop', crop);
30718         }
30719         
30720         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30721             formData.append(this.paramName, file, file.name);
30722         }
30723         
30724         if(typeof(file.filename) != 'undefined'){
30725             formData.append('filename', file.filename);
30726         }
30727         
30728         if(typeof(file.mimetype) != 'undefined'){
30729             formData.append('mimetype', file.mimetype);
30730         }
30731         
30732         if(this.fireEvent('arrange', this, formData) != false){
30733             this.xhr.send(formData);
30734         };
30735     },
30736     
30737     xhrOnLoad : function(xhr)
30738     {
30739         if(this.loadMask){
30740             this.maskEl.unmask();
30741         }
30742         
30743         if (xhr.readyState !== 4) {
30744             this.fireEvent('exception', this, xhr);
30745             return;
30746         }
30747
30748         var response = Roo.decode(xhr.responseText);
30749         
30750         if(!response.success){
30751             this.fireEvent('exception', this, xhr);
30752             return;
30753         }
30754         
30755         var response = Roo.decode(xhr.responseText);
30756         
30757         this.fireEvent('upload', this, response);
30758         
30759     },
30760     
30761     xhrOnError : function()
30762     {
30763         if(this.loadMask){
30764             this.maskEl.unmask();
30765         }
30766         
30767         Roo.log('xhr on error');
30768         
30769         var response = Roo.decode(xhr.responseText);
30770           
30771         Roo.log(response);
30772         
30773     },
30774     
30775     prepare : function(file)
30776     {   
30777         if(this.loadMask){
30778             this.maskEl.mask(this.loadingText);
30779         }
30780         
30781         this.file = false;
30782         this.exif = {};
30783         
30784         if(typeof(file) === 'string'){
30785             this.loadCanvas(file);
30786             return;
30787         }
30788         
30789         if(!file || !this.urlAPI){
30790             return;
30791         }
30792         
30793         this.file = file;
30794         this.cropType = file.type;
30795         
30796         var _this = this;
30797         
30798         if(this.fireEvent('prepare', this, this.file) != false){
30799             
30800             var reader = new FileReader();
30801             
30802             reader.onload = function (e) {
30803                 if (e.target.error) {
30804                     Roo.log(e.target.error);
30805                     return;
30806                 }
30807                 
30808                 var buffer = e.target.result,
30809                     dataView = new DataView(buffer),
30810                     offset = 2,
30811                     maxOffset = dataView.byteLength - 4,
30812                     markerBytes,
30813                     markerLength;
30814                 
30815                 if (dataView.getUint16(0) === 0xffd8) {
30816                     while (offset < maxOffset) {
30817                         markerBytes = dataView.getUint16(offset);
30818                         
30819                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30820                             markerLength = dataView.getUint16(offset + 2) + 2;
30821                             if (offset + markerLength > dataView.byteLength) {
30822                                 Roo.log('Invalid meta data: Invalid segment size.');
30823                                 break;
30824                             }
30825                             
30826                             if(markerBytes == 0xffe1){
30827                                 _this.parseExifData(
30828                                     dataView,
30829                                     offset,
30830                                     markerLength
30831                                 );
30832                             }
30833                             
30834                             offset += markerLength;
30835                             
30836                             continue;
30837                         }
30838                         
30839                         break;
30840                     }
30841                     
30842                 }
30843                 
30844                 var url = _this.urlAPI.createObjectURL(_this.file);
30845                 
30846                 _this.loadCanvas(url);
30847                 
30848                 return;
30849             }
30850             
30851             reader.readAsArrayBuffer(this.file);
30852             
30853         }
30854         
30855     },
30856     
30857     parseExifData : function(dataView, offset, length)
30858     {
30859         var tiffOffset = offset + 10,
30860             littleEndian,
30861             dirOffset;
30862     
30863         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30864             // No Exif data, might be XMP data instead
30865             return;
30866         }
30867         
30868         // Check for the ASCII code for "Exif" (0x45786966):
30869         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30870             // No Exif data, might be XMP data instead
30871             return;
30872         }
30873         if (tiffOffset + 8 > dataView.byteLength) {
30874             Roo.log('Invalid Exif data: Invalid segment size.');
30875             return;
30876         }
30877         // Check for the two null bytes:
30878         if (dataView.getUint16(offset + 8) !== 0x0000) {
30879             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30880             return;
30881         }
30882         // Check the byte alignment:
30883         switch (dataView.getUint16(tiffOffset)) {
30884         case 0x4949:
30885             littleEndian = true;
30886             break;
30887         case 0x4D4D:
30888             littleEndian = false;
30889             break;
30890         default:
30891             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30892             return;
30893         }
30894         // Check for the TIFF tag marker (0x002A):
30895         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30896             Roo.log('Invalid Exif data: Missing TIFF marker.');
30897             return;
30898         }
30899         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30900         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30901         
30902         this.parseExifTags(
30903             dataView,
30904             tiffOffset,
30905             tiffOffset + dirOffset,
30906             littleEndian
30907         );
30908     },
30909     
30910     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30911     {
30912         var tagsNumber,
30913             dirEndOffset,
30914             i;
30915         if (dirOffset + 6 > dataView.byteLength) {
30916             Roo.log('Invalid Exif data: Invalid directory offset.');
30917             return;
30918         }
30919         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30920         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30921         if (dirEndOffset + 4 > dataView.byteLength) {
30922             Roo.log('Invalid Exif data: Invalid directory size.');
30923             return;
30924         }
30925         for (i = 0; i < tagsNumber; i += 1) {
30926             this.parseExifTag(
30927                 dataView,
30928                 tiffOffset,
30929                 dirOffset + 2 + 12 * i, // tag offset
30930                 littleEndian
30931             );
30932         }
30933         // Return the offset to the next directory:
30934         return dataView.getUint32(dirEndOffset, littleEndian);
30935     },
30936     
30937     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30938     {
30939         var tag = dataView.getUint16(offset, littleEndian);
30940         
30941         this.exif[tag] = this.getExifValue(
30942             dataView,
30943             tiffOffset,
30944             offset,
30945             dataView.getUint16(offset + 2, littleEndian), // tag type
30946             dataView.getUint32(offset + 4, littleEndian), // tag length
30947             littleEndian
30948         );
30949     },
30950     
30951     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30952     {
30953         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30954             tagSize,
30955             dataOffset,
30956             values,
30957             i,
30958             str,
30959             c;
30960     
30961         if (!tagType) {
30962             Roo.log('Invalid Exif data: Invalid tag type.');
30963             return;
30964         }
30965         
30966         tagSize = tagType.size * length;
30967         // Determine if the value is contained in the dataOffset bytes,
30968         // or if the value at the dataOffset is a pointer to the actual data:
30969         dataOffset = tagSize > 4 ?
30970                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30971         if (dataOffset + tagSize > dataView.byteLength) {
30972             Roo.log('Invalid Exif data: Invalid data offset.');
30973             return;
30974         }
30975         if (length === 1) {
30976             return tagType.getValue(dataView, dataOffset, littleEndian);
30977         }
30978         values = [];
30979         for (i = 0; i < length; i += 1) {
30980             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30981         }
30982         
30983         if (tagType.ascii) {
30984             str = '';
30985             // Concatenate the chars:
30986             for (i = 0; i < values.length; i += 1) {
30987                 c = values[i];
30988                 // Ignore the terminating NULL byte(s):
30989                 if (c === '\u0000') {
30990                     break;
30991                 }
30992                 str += c;
30993             }
30994             return str;
30995         }
30996         return values;
30997     }
30998     
30999 });
31000
31001 Roo.apply(Roo.bootstrap.UploadCropbox, {
31002     tags : {
31003         'Orientation': 0x0112
31004     },
31005     
31006     Orientation: {
31007             1: 0, //'top-left',
31008 //            2: 'top-right',
31009             3: 180, //'bottom-right',
31010 //            4: 'bottom-left',
31011 //            5: 'left-top',
31012             6: 90, //'right-top',
31013 //            7: 'right-bottom',
31014             8: 270 //'left-bottom'
31015     },
31016     
31017     exifTagTypes : {
31018         // byte, 8-bit unsigned int:
31019         1: {
31020             getValue: function (dataView, dataOffset) {
31021                 return dataView.getUint8(dataOffset);
31022             },
31023             size: 1
31024         },
31025         // ascii, 8-bit byte:
31026         2: {
31027             getValue: function (dataView, dataOffset) {
31028                 return String.fromCharCode(dataView.getUint8(dataOffset));
31029             },
31030             size: 1,
31031             ascii: true
31032         },
31033         // short, 16 bit int:
31034         3: {
31035             getValue: function (dataView, dataOffset, littleEndian) {
31036                 return dataView.getUint16(dataOffset, littleEndian);
31037             },
31038             size: 2
31039         },
31040         // long, 32 bit int:
31041         4: {
31042             getValue: function (dataView, dataOffset, littleEndian) {
31043                 return dataView.getUint32(dataOffset, littleEndian);
31044             },
31045             size: 4
31046         },
31047         // rational = two long values, first is numerator, second is denominator:
31048         5: {
31049             getValue: function (dataView, dataOffset, littleEndian) {
31050                 return dataView.getUint32(dataOffset, littleEndian) /
31051                     dataView.getUint32(dataOffset + 4, littleEndian);
31052             },
31053             size: 8
31054         },
31055         // slong, 32 bit signed int:
31056         9: {
31057             getValue: function (dataView, dataOffset, littleEndian) {
31058                 return dataView.getInt32(dataOffset, littleEndian);
31059             },
31060             size: 4
31061         },
31062         // srational, two slongs, first is numerator, second is denominator:
31063         10: {
31064             getValue: function (dataView, dataOffset, littleEndian) {
31065                 return dataView.getInt32(dataOffset, littleEndian) /
31066                     dataView.getInt32(dataOffset + 4, littleEndian);
31067             },
31068             size: 8
31069         }
31070     },
31071     
31072     footer : {
31073         STANDARD : [
31074             {
31075                 tag : 'div',
31076                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31077                 action : 'rotate-left',
31078                 cn : [
31079                     {
31080                         tag : 'button',
31081                         cls : 'btn btn-default',
31082                         html : '<i class="fa fa-undo"></i>'
31083                     }
31084                 ]
31085             },
31086             {
31087                 tag : 'div',
31088                 cls : 'btn-group roo-upload-cropbox-picture',
31089                 action : 'picture',
31090                 cn : [
31091                     {
31092                         tag : 'button',
31093                         cls : 'btn btn-default',
31094                         html : '<i class="fa fa-picture-o"></i>'
31095                     }
31096                 ]
31097             },
31098             {
31099                 tag : 'div',
31100                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31101                 action : 'rotate-right',
31102                 cn : [
31103                     {
31104                         tag : 'button',
31105                         cls : 'btn btn-default',
31106                         html : '<i class="fa fa-repeat"></i>'
31107                     }
31108                 ]
31109             }
31110         ],
31111         DOCUMENT : [
31112             {
31113                 tag : 'div',
31114                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31115                 action : 'rotate-left',
31116                 cn : [
31117                     {
31118                         tag : 'button',
31119                         cls : 'btn btn-default',
31120                         html : '<i class="fa fa-undo"></i>'
31121                     }
31122                 ]
31123             },
31124             {
31125                 tag : 'div',
31126                 cls : 'btn-group roo-upload-cropbox-download',
31127                 action : 'download',
31128                 cn : [
31129                     {
31130                         tag : 'button',
31131                         cls : 'btn btn-default',
31132                         html : '<i class="fa fa-download"></i>'
31133                     }
31134                 ]
31135             },
31136             {
31137                 tag : 'div',
31138                 cls : 'btn-group roo-upload-cropbox-crop',
31139                 action : 'crop',
31140                 cn : [
31141                     {
31142                         tag : 'button',
31143                         cls : 'btn btn-default',
31144                         html : '<i class="fa fa-crop"></i>'
31145                     }
31146                 ]
31147             },
31148             {
31149                 tag : 'div',
31150                 cls : 'btn-group roo-upload-cropbox-trash',
31151                 action : 'trash',
31152                 cn : [
31153                     {
31154                         tag : 'button',
31155                         cls : 'btn btn-default',
31156                         html : '<i class="fa fa-trash"></i>'
31157                     }
31158                 ]
31159             },
31160             {
31161                 tag : 'div',
31162                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31163                 action : 'rotate-right',
31164                 cn : [
31165                     {
31166                         tag : 'button',
31167                         cls : 'btn btn-default',
31168                         html : '<i class="fa fa-repeat"></i>'
31169                     }
31170                 ]
31171             }
31172         ],
31173         ROTATOR : [
31174             {
31175                 tag : 'div',
31176                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31177                 action : 'rotate-left',
31178                 cn : [
31179                     {
31180                         tag : 'button',
31181                         cls : 'btn btn-default',
31182                         html : '<i class="fa fa-undo"></i>'
31183                     }
31184                 ]
31185             },
31186             {
31187                 tag : 'div',
31188                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31189                 action : 'rotate-right',
31190                 cn : [
31191                     {
31192                         tag : 'button',
31193                         cls : 'btn btn-default',
31194                         html : '<i class="fa fa-repeat"></i>'
31195                     }
31196                 ]
31197             }
31198         ]
31199     }
31200 });
31201
31202 /*
31203 * Licence: LGPL
31204 */
31205
31206 /**
31207  * @class Roo.bootstrap.DocumentManager
31208  * @extends Roo.bootstrap.Component
31209  * Bootstrap DocumentManager class
31210  * @cfg {String} paramName default 'imageUpload'
31211  * @cfg {String} toolTipName default 'filename'
31212  * @cfg {String} method default POST
31213  * @cfg {String} url action url
31214  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31215  * @cfg {Boolean} multiple multiple upload default true
31216  * @cfg {Number} thumbSize default 300
31217  * @cfg {String} fieldLabel
31218  * @cfg {Number} labelWidth default 4
31219  * @cfg {String} labelAlign (left|top) default left
31220  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31221 * @cfg {Number} labellg set the width of label (1-12)
31222  * @cfg {Number} labelmd set the width of label (1-12)
31223  * @cfg {Number} labelsm set the width of label (1-12)
31224  * @cfg {Number} labelxs set the width of label (1-12)
31225  * 
31226  * @constructor
31227  * Create a new DocumentManager
31228  * @param {Object} config The config object
31229  */
31230
31231 Roo.bootstrap.DocumentManager = function(config){
31232     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31233     
31234     this.files = [];
31235     this.delegates = [];
31236     
31237     this.addEvents({
31238         /**
31239          * @event initial
31240          * Fire when initial the DocumentManager
31241          * @param {Roo.bootstrap.DocumentManager} this
31242          */
31243         "initial" : true,
31244         /**
31245          * @event inspect
31246          * inspect selected file
31247          * @param {Roo.bootstrap.DocumentManager} this
31248          * @param {File} file
31249          */
31250         "inspect" : true,
31251         /**
31252          * @event exception
31253          * Fire when xhr load exception
31254          * @param {Roo.bootstrap.DocumentManager} this
31255          * @param {XMLHttpRequest} xhr
31256          */
31257         "exception" : true,
31258         /**
31259          * @event afterupload
31260          * Fire when xhr load exception
31261          * @param {Roo.bootstrap.DocumentManager} this
31262          * @param {XMLHttpRequest} xhr
31263          */
31264         "afterupload" : true,
31265         /**
31266          * @event prepare
31267          * prepare the form data
31268          * @param {Roo.bootstrap.DocumentManager} this
31269          * @param {Object} formData
31270          */
31271         "prepare" : true,
31272         /**
31273          * @event remove
31274          * Fire when remove the file
31275          * @param {Roo.bootstrap.DocumentManager} this
31276          * @param {Object} file
31277          */
31278         "remove" : true,
31279         /**
31280          * @event refresh
31281          * Fire after refresh the file
31282          * @param {Roo.bootstrap.DocumentManager} this
31283          */
31284         "refresh" : true,
31285         /**
31286          * @event click
31287          * Fire after click the image
31288          * @param {Roo.bootstrap.DocumentManager} this
31289          * @param {Object} file
31290          */
31291         "click" : true,
31292         /**
31293          * @event edit
31294          * Fire when upload a image and editable set to true
31295          * @param {Roo.bootstrap.DocumentManager} this
31296          * @param {Object} file
31297          */
31298         "edit" : true,
31299         /**
31300          * @event beforeselectfile
31301          * Fire before select file
31302          * @param {Roo.bootstrap.DocumentManager} this
31303          */
31304         "beforeselectfile" : true,
31305         /**
31306          * @event process
31307          * Fire before process file
31308          * @param {Roo.bootstrap.DocumentManager} this
31309          * @param {Object} file
31310          */
31311         "process" : true,
31312         /**
31313          * @event previewrendered
31314          * Fire when preview rendered
31315          * @param {Roo.bootstrap.DocumentManager} this
31316          * @param {Object} file
31317          */
31318         "previewrendered" : true,
31319         /**
31320          */
31321         "previewResize" : true
31322         
31323     });
31324 };
31325
31326 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31327     
31328     boxes : 0,
31329     inputName : '',
31330     thumbSize : 300,
31331     multiple : true,
31332     files : false,
31333     method : 'POST',
31334     url : '',
31335     paramName : 'imageUpload',
31336     toolTipName : 'filename',
31337     fieldLabel : '',
31338     labelWidth : 4,
31339     labelAlign : 'left',
31340     editable : true,
31341     delegates : false,
31342     xhr : false, 
31343     
31344     labellg : 0,
31345     labelmd : 0,
31346     labelsm : 0,
31347     labelxs : 0,
31348     
31349     getAutoCreate : function()
31350     {   
31351         var managerWidget = {
31352             tag : 'div',
31353             cls : 'roo-document-manager',
31354             cn : [
31355                 {
31356                     tag : 'input',
31357                     cls : 'roo-document-manager-selector',
31358                     type : 'file'
31359                 },
31360                 {
31361                     tag : 'div',
31362                     cls : 'roo-document-manager-uploader',
31363                     cn : [
31364                         {
31365                             tag : 'div',
31366                             cls : 'roo-document-manager-upload-btn',
31367                             html : '<i class="fa fa-plus"></i>'
31368                         }
31369                     ]
31370                     
31371                 }
31372             ]
31373         };
31374         
31375         var content = [
31376             {
31377                 tag : 'div',
31378                 cls : 'column col-md-12',
31379                 cn : managerWidget
31380             }
31381         ];
31382         
31383         if(this.fieldLabel.length){
31384             
31385             content = [
31386                 {
31387                     tag : 'div',
31388                     cls : 'column col-md-12',
31389                     html : this.fieldLabel
31390                 },
31391                 {
31392                     tag : 'div',
31393                     cls : 'column col-md-12',
31394                     cn : managerWidget
31395                 }
31396             ];
31397
31398             if(this.labelAlign == 'left'){
31399                 content = [
31400                     {
31401                         tag : 'div',
31402                         cls : 'column',
31403                         html : this.fieldLabel
31404                     },
31405                     {
31406                         tag : 'div',
31407                         cls : 'column',
31408                         cn : managerWidget
31409                     }
31410                 ];
31411                 
31412                 if(this.labelWidth > 12){
31413                     content[0].style = "width: " + this.labelWidth + 'px';
31414                 }
31415
31416                 if(this.labelWidth < 13 && this.labelmd == 0){
31417                     this.labelmd = this.labelWidth;
31418                 }
31419
31420                 if(this.labellg > 0){
31421                     content[0].cls += ' col-lg-' + this.labellg;
31422                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31423                 }
31424
31425                 if(this.labelmd > 0){
31426                     content[0].cls += ' col-md-' + this.labelmd;
31427                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31428                 }
31429
31430                 if(this.labelsm > 0){
31431                     content[0].cls += ' col-sm-' + this.labelsm;
31432                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31433                 }
31434
31435                 if(this.labelxs > 0){
31436                     content[0].cls += ' col-xs-' + this.labelxs;
31437                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31438                 }
31439                 
31440             }
31441         }
31442         
31443         var cfg = {
31444             tag : 'div',
31445             cls : 'row clearfix',
31446             cn : content
31447         };
31448         
31449         return cfg;
31450         
31451     },
31452     
31453     initEvents : function()
31454     {
31455         this.managerEl = this.el.select('.roo-document-manager', true).first();
31456         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31457         
31458         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31459         this.selectorEl.hide();
31460         
31461         if(this.multiple){
31462             this.selectorEl.attr('multiple', 'multiple');
31463         }
31464         
31465         this.selectorEl.on('change', this.onFileSelected, this);
31466         
31467         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31468         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31469         
31470         this.uploader.on('click', this.onUploaderClick, this);
31471         
31472         this.renderProgressDialog();
31473         
31474         var _this = this;
31475         
31476         window.addEventListener("resize", function() { _this.refresh(); } );
31477         
31478         this.fireEvent('initial', this);
31479     },
31480     
31481     renderProgressDialog : function()
31482     {
31483         var _this = this;
31484         
31485         this.progressDialog = new Roo.bootstrap.Modal({
31486             cls : 'roo-document-manager-progress-dialog',
31487             allow_close : false,
31488             animate : false,
31489             title : '',
31490             buttons : [
31491                 {
31492                     name  :'cancel',
31493                     weight : 'danger',
31494                     html : 'Cancel'
31495                 }
31496             ], 
31497             listeners : { 
31498                 btnclick : function() {
31499                     _this.uploadCancel();
31500                     this.hide();
31501                 }
31502             }
31503         });
31504          
31505         this.progressDialog.render(Roo.get(document.body));
31506          
31507         this.progress = new Roo.bootstrap.Progress({
31508             cls : 'roo-document-manager-progress',
31509             active : true,
31510             striped : true
31511         });
31512         
31513         this.progress.render(this.progressDialog.getChildContainer());
31514         
31515         this.progressBar = new Roo.bootstrap.ProgressBar({
31516             cls : 'roo-document-manager-progress-bar',
31517             aria_valuenow : 0,
31518             aria_valuemin : 0,
31519             aria_valuemax : 12,
31520             panel : 'success'
31521         });
31522         
31523         this.progressBar.render(this.progress.getChildContainer());
31524     },
31525     
31526     onUploaderClick : function(e)
31527     {
31528         e.preventDefault();
31529      
31530         if(this.fireEvent('beforeselectfile', this) != false){
31531             this.selectorEl.dom.click();
31532         }
31533         
31534     },
31535     
31536     onFileSelected : function(e)
31537     {
31538         e.preventDefault();
31539         
31540         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31541             return;
31542         }
31543         
31544         Roo.each(this.selectorEl.dom.files, function(file){
31545             if(this.fireEvent('inspect', this, file) != false){
31546                 this.files.push(file);
31547             }
31548         }, this);
31549         
31550         this.queue();
31551         
31552     },
31553     
31554     queue : function()
31555     {
31556         this.selectorEl.dom.value = '';
31557         
31558         if(!this.files || !this.files.length){
31559             return;
31560         }
31561         
31562         if(this.boxes > 0 && this.files.length > this.boxes){
31563             this.files = this.files.slice(0, this.boxes);
31564         }
31565         
31566         this.uploader.show();
31567         
31568         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31569             this.uploader.hide();
31570         }
31571         
31572         var _this = this;
31573         
31574         var files = [];
31575         
31576         var docs = [];
31577         
31578         Roo.each(this.files, function(file){
31579             
31580             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31581                 var f = this.renderPreview(file);
31582                 files.push(f);
31583                 return;
31584             }
31585             
31586             if(file.type.indexOf('image') != -1){
31587                 this.delegates.push(
31588                     (function(){
31589                         _this.process(file);
31590                     }).createDelegate(this)
31591                 );
31592         
31593                 return;
31594             }
31595             
31596             docs.push(
31597                 (function(){
31598                     _this.process(file);
31599                 }).createDelegate(this)
31600             );
31601             
31602         }, this);
31603         
31604         this.files = files;
31605         
31606         this.delegates = this.delegates.concat(docs);
31607         
31608         if(!this.delegates.length){
31609             this.refresh();
31610             return;
31611         }
31612         
31613         this.progressBar.aria_valuemax = this.delegates.length;
31614         
31615         this.arrange();
31616         
31617         return;
31618     },
31619     
31620     arrange : function()
31621     {
31622         if(!this.delegates.length){
31623             this.progressDialog.hide();
31624             this.refresh();
31625             return;
31626         }
31627         
31628         var delegate = this.delegates.shift();
31629         
31630         this.progressDialog.show();
31631         
31632         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31633         
31634         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31635         
31636         delegate();
31637     },
31638     
31639     refresh : function()
31640     {
31641         this.uploader.show();
31642         
31643         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31644             this.uploader.hide();
31645         }
31646         
31647         Roo.isTouch ? this.closable(false) : this.closable(true);
31648         
31649         this.fireEvent('refresh', this);
31650     },
31651     
31652     onRemove : function(e, el, o)
31653     {
31654         e.preventDefault();
31655         
31656         this.fireEvent('remove', this, o);
31657         
31658     },
31659     
31660     remove : function(o)
31661     {
31662         var files = [];
31663         
31664         Roo.each(this.files, function(file){
31665             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31666                 files.push(file);
31667                 return;
31668             }
31669
31670             o.target.remove();
31671
31672         }, this);
31673         
31674         this.files = files;
31675         
31676         this.refresh();
31677     },
31678     
31679     clear : function()
31680     {
31681         Roo.each(this.files, function(file){
31682             if(!file.target){
31683                 return;
31684             }
31685             
31686             file.target.remove();
31687
31688         }, this);
31689         
31690         this.files = [];
31691         
31692         this.refresh();
31693     },
31694     
31695     onClick : function(e, el, o)
31696     {
31697         e.preventDefault();
31698         
31699         this.fireEvent('click', this, o);
31700         
31701     },
31702     
31703     closable : function(closable)
31704     {
31705         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31706             
31707             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31708             
31709             if(closable){
31710                 el.show();
31711                 return;
31712             }
31713             
31714             el.hide();
31715             
31716         }, this);
31717     },
31718     
31719     xhrOnLoad : function(xhr)
31720     {
31721         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31722             el.remove();
31723         }, this);
31724         
31725         if (xhr.readyState !== 4) {
31726             this.arrange();
31727             this.fireEvent('exception', this, xhr);
31728             return;
31729         }
31730
31731         var response = Roo.decode(xhr.responseText);
31732         
31733         if(!response.success){
31734             this.arrange();
31735             this.fireEvent('exception', this, xhr);
31736             return;
31737         }
31738         
31739         var file = this.renderPreview(response.data);
31740         
31741         this.files.push(file);
31742         
31743         this.arrange();
31744         
31745         this.fireEvent('afterupload', this, xhr);
31746         
31747     },
31748     
31749     xhrOnError : function(xhr)
31750     {
31751         Roo.log('xhr on error');
31752         
31753         var response = Roo.decode(xhr.responseText);
31754           
31755         Roo.log(response);
31756         
31757         this.arrange();
31758     },
31759     
31760     process : function(file)
31761     {
31762         if(this.fireEvent('process', this, file) !== false){
31763             if(this.editable && file.type.indexOf('image') != -1){
31764                 this.fireEvent('edit', this, file);
31765                 return;
31766             }
31767
31768             this.uploadStart(file, false);
31769
31770             return;
31771         }
31772         
31773     },
31774     
31775     uploadStart : function(file, crop)
31776     {
31777         this.xhr = new XMLHttpRequest();
31778         
31779         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31780             this.arrange();
31781             return;
31782         }
31783         
31784         file.xhr = this.xhr;
31785             
31786         this.managerEl.createChild({
31787             tag : 'div',
31788             cls : 'roo-document-manager-loading',
31789             cn : [
31790                 {
31791                     tag : 'div',
31792                     tooltip : file.name,
31793                     cls : 'roo-document-manager-thumb',
31794                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31795                 }
31796             ]
31797
31798         });
31799
31800         this.xhr.open(this.method, this.url, true);
31801         
31802         var headers = {
31803             "Accept": "application/json",
31804             "Cache-Control": "no-cache",
31805             "X-Requested-With": "XMLHttpRequest"
31806         };
31807         
31808         for (var headerName in headers) {
31809             var headerValue = headers[headerName];
31810             if (headerValue) {
31811                 this.xhr.setRequestHeader(headerName, headerValue);
31812             }
31813         }
31814         
31815         var _this = this;
31816         
31817         this.xhr.onload = function()
31818         {
31819             _this.xhrOnLoad(_this.xhr);
31820         }
31821         
31822         this.xhr.onerror = function()
31823         {
31824             _this.xhrOnError(_this.xhr);
31825         }
31826         
31827         var formData = new FormData();
31828
31829         formData.append('returnHTML', 'NO');
31830         
31831         if(crop){
31832             formData.append('crop', crop);
31833         }
31834         
31835         formData.append(this.paramName, file, file.name);
31836         
31837         var options = {
31838             file : file, 
31839             manually : false
31840         };
31841         
31842         if(this.fireEvent('prepare', this, formData, options) != false){
31843             
31844             if(options.manually){
31845                 return;
31846             }
31847             
31848             this.xhr.send(formData);
31849             return;
31850         };
31851         
31852         this.uploadCancel();
31853     },
31854     
31855     uploadCancel : function()
31856     {
31857         if (this.xhr) {
31858             this.xhr.abort();
31859         }
31860         
31861         this.delegates = [];
31862         
31863         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31864             el.remove();
31865         }, this);
31866         
31867         this.arrange();
31868     },
31869     
31870     renderPreview : function(file)
31871     {
31872         if(typeof(file.target) != 'undefined' && file.target){
31873             return file;
31874         }
31875         
31876         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31877         
31878         var previewEl = this.managerEl.createChild({
31879             tag : 'div',
31880             cls : 'roo-document-manager-preview',
31881             cn : [
31882                 {
31883                     tag : 'div',
31884                     tooltip : file[this.toolTipName],
31885                     cls : 'roo-document-manager-thumb',
31886                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31887                 },
31888                 {
31889                     tag : 'button',
31890                     cls : 'close',
31891                     html : '<i class="fa fa-times-circle"></i>'
31892                 }
31893             ]
31894         });
31895
31896         var close = previewEl.select('button.close', true).first();
31897
31898         close.on('click', this.onRemove, this, file);
31899
31900         file.target = previewEl;
31901
31902         var image = previewEl.select('img', true).first();
31903         
31904         var _this = this;
31905         
31906         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31907         
31908         image.on('click', this.onClick, this, file);
31909         
31910         this.fireEvent('previewrendered', this, file);
31911         
31912         return file;
31913         
31914     },
31915     
31916     onPreviewLoad : function(file, image)
31917     {
31918         if(typeof(file.target) == 'undefined' || !file.target){
31919             return;
31920         }
31921         
31922         var width = image.dom.naturalWidth || image.dom.width;
31923         var height = image.dom.naturalHeight || image.dom.height;
31924         
31925         if(!this.previewResize) {
31926             return;
31927         }
31928         
31929         if(width > height){
31930             file.target.addClass('wide');
31931             return;
31932         }
31933         
31934         file.target.addClass('tall');
31935         return;
31936         
31937     },
31938     
31939     uploadFromSource : function(file, crop)
31940     {
31941         this.xhr = new XMLHttpRequest();
31942         
31943         this.managerEl.createChild({
31944             tag : 'div',
31945             cls : 'roo-document-manager-loading',
31946             cn : [
31947                 {
31948                     tag : 'div',
31949                     tooltip : file.name,
31950                     cls : 'roo-document-manager-thumb',
31951                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31952                 }
31953             ]
31954
31955         });
31956
31957         this.xhr.open(this.method, this.url, true);
31958         
31959         var headers = {
31960             "Accept": "application/json",
31961             "Cache-Control": "no-cache",
31962             "X-Requested-With": "XMLHttpRequest"
31963         };
31964         
31965         for (var headerName in headers) {
31966             var headerValue = headers[headerName];
31967             if (headerValue) {
31968                 this.xhr.setRequestHeader(headerName, headerValue);
31969             }
31970         }
31971         
31972         var _this = this;
31973         
31974         this.xhr.onload = function()
31975         {
31976             _this.xhrOnLoad(_this.xhr);
31977         }
31978         
31979         this.xhr.onerror = function()
31980         {
31981             _this.xhrOnError(_this.xhr);
31982         }
31983         
31984         var formData = new FormData();
31985
31986         formData.append('returnHTML', 'NO');
31987         
31988         formData.append('crop', crop);
31989         
31990         if(typeof(file.filename) != 'undefined'){
31991             formData.append('filename', file.filename);
31992         }
31993         
31994         if(typeof(file.mimetype) != 'undefined'){
31995             formData.append('mimetype', file.mimetype);
31996         }
31997         
31998         Roo.log(formData);
31999         
32000         if(this.fireEvent('prepare', this, formData) != false){
32001             this.xhr.send(formData);
32002         };
32003     }
32004 });
32005
32006 /*
32007 * Licence: LGPL
32008 */
32009
32010 /**
32011  * @class Roo.bootstrap.DocumentViewer
32012  * @extends Roo.bootstrap.Component
32013  * Bootstrap DocumentViewer class
32014  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32015  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32016  * 
32017  * @constructor
32018  * Create a new DocumentViewer
32019  * @param {Object} config The config object
32020  */
32021
32022 Roo.bootstrap.DocumentViewer = function(config){
32023     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32024     
32025     this.addEvents({
32026         /**
32027          * @event initial
32028          * Fire after initEvent
32029          * @param {Roo.bootstrap.DocumentViewer} this
32030          */
32031         "initial" : true,
32032         /**
32033          * @event click
32034          * Fire after click
32035          * @param {Roo.bootstrap.DocumentViewer} this
32036          */
32037         "click" : true,
32038         /**
32039          * @event download
32040          * Fire after download button
32041          * @param {Roo.bootstrap.DocumentViewer} this
32042          */
32043         "download" : true,
32044         /**
32045          * @event trash
32046          * Fire after trash button
32047          * @param {Roo.bootstrap.DocumentViewer} this
32048          */
32049         "trash" : true
32050         
32051     });
32052 };
32053
32054 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32055     
32056     showDownload : true,
32057     
32058     showTrash : true,
32059     
32060     getAutoCreate : function()
32061     {
32062         var cfg = {
32063             tag : 'div',
32064             cls : 'roo-document-viewer',
32065             cn : [
32066                 {
32067                     tag : 'div',
32068                     cls : 'roo-document-viewer-body',
32069                     cn : [
32070                         {
32071                             tag : 'div',
32072                             cls : 'roo-document-viewer-thumb',
32073                             cn : [
32074                                 {
32075                                     tag : 'img',
32076                                     cls : 'roo-document-viewer-image'
32077                                 }
32078                             ]
32079                         }
32080                     ]
32081                 },
32082                 {
32083                     tag : 'div',
32084                     cls : 'roo-document-viewer-footer',
32085                     cn : {
32086                         tag : 'div',
32087                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32088                         cn : [
32089                             {
32090                                 tag : 'div',
32091                                 cls : 'btn-group roo-document-viewer-download',
32092                                 cn : [
32093                                     {
32094                                         tag : 'button',
32095                                         cls : 'btn btn-default',
32096                                         html : '<i class="fa fa-download"></i>'
32097                                     }
32098                                 ]
32099                             },
32100                             {
32101                                 tag : 'div',
32102                                 cls : 'btn-group roo-document-viewer-trash',
32103                                 cn : [
32104                                     {
32105                                         tag : 'button',
32106                                         cls : 'btn btn-default',
32107                                         html : '<i class="fa fa-trash"></i>'
32108                                     }
32109                                 ]
32110                             }
32111                         ]
32112                     }
32113                 }
32114             ]
32115         };
32116         
32117         return cfg;
32118     },
32119     
32120     initEvents : function()
32121     {
32122         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32123         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32124         
32125         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32126         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32127         
32128         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32129         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32130         
32131         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32132         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32133         
32134         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32135         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32136         
32137         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32138         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32139         
32140         this.bodyEl.on('click', this.onClick, this);
32141         this.downloadBtn.on('click', this.onDownload, this);
32142         this.trashBtn.on('click', this.onTrash, this);
32143         
32144         this.downloadBtn.hide();
32145         this.trashBtn.hide();
32146         
32147         if(this.showDownload){
32148             this.downloadBtn.show();
32149         }
32150         
32151         if(this.showTrash){
32152             this.trashBtn.show();
32153         }
32154         
32155         if(!this.showDownload && !this.showTrash) {
32156             this.footerEl.hide();
32157         }
32158         
32159     },
32160     
32161     initial : function()
32162     {
32163         this.fireEvent('initial', this);
32164         
32165     },
32166     
32167     onClick : function(e)
32168     {
32169         e.preventDefault();
32170         
32171         this.fireEvent('click', this);
32172     },
32173     
32174     onDownload : function(e)
32175     {
32176         e.preventDefault();
32177         
32178         this.fireEvent('download', this);
32179     },
32180     
32181     onTrash : function(e)
32182     {
32183         e.preventDefault();
32184         
32185         this.fireEvent('trash', this);
32186     }
32187     
32188 });
32189 /*
32190  * - LGPL
32191  *
32192  * nav progress bar
32193  * 
32194  */
32195
32196 /**
32197  * @class Roo.bootstrap.NavProgressBar
32198  * @extends Roo.bootstrap.Component
32199  * Bootstrap NavProgressBar class
32200  * 
32201  * @constructor
32202  * Create a new nav progress bar
32203  * @param {Object} config The config object
32204  */
32205
32206 Roo.bootstrap.NavProgressBar = function(config){
32207     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32208
32209     this.bullets = this.bullets || [];
32210    
32211 //    Roo.bootstrap.NavProgressBar.register(this);
32212      this.addEvents({
32213         /**
32214              * @event changed
32215              * Fires when the active item changes
32216              * @param {Roo.bootstrap.NavProgressBar} this
32217              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32218              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32219          */
32220         'changed': true
32221      });
32222     
32223 };
32224
32225 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32226     
32227     bullets : [],
32228     barItems : [],
32229     
32230     getAutoCreate : function()
32231     {
32232         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32233         
32234         cfg = {
32235             tag : 'div',
32236             cls : 'roo-navigation-bar-group',
32237             cn : [
32238                 {
32239                     tag : 'div',
32240                     cls : 'roo-navigation-top-bar'
32241                 },
32242                 {
32243                     tag : 'div',
32244                     cls : 'roo-navigation-bullets-bar',
32245                     cn : [
32246                         {
32247                             tag : 'ul',
32248                             cls : 'roo-navigation-bar'
32249                         }
32250                     ]
32251                 },
32252                 
32253                 {
32254                     tag : 'div',
32255                     cls : 'roo-navigation-bottom-bar'
32256                 }
32257             ]
32258             
32259         };
32260         
32261         return cfg;
32262         
32263     },
32264     
32265     initEvents: function() 
32266     {
32267         
32268     },
32269     
32270     onRender : function(ct, position) 
32271     {
32272         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32273         
32274         if(this.bullets.length){
32275             Roo.each(this.bullets, function(b){
32276                this.addItem(b);
32277             }, this);
32278         }
32279         
32280         this.format();
32281         
32282     },
32283     
32284     addItem : function(cfg)
32285     {
32286         var item = new Roo.bootstrap.NavProgressItem(cfg);
32287         
32288         item.parentId = this.id;
32289         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32290         
32291         if(cfg.html){
32292             var top = new Roo.bootstrap.Element({
32293                 tag : 'div',
32294                 cls : 'roo-navigation-bar-text'
32295             });
32296             
32297             var bottom = new Roo.bootstrap.Element({
32298                 tag : 'div',
32299                 cls : 'roo-navigation-bar-text'
32300             });
32301             
32302             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32303             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32304             
32305             var topText = new Roo.bootstrap.Element({
32306                 tag : 'span',
32307                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32308             });
32309             
32310             var bottomText = new Roo.bootstrap.Element({
32311                 tag : 'span',
32312                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32313             });
32314             
32315             topText.onRender(top.el, null);
32316             bottomText.onRender(bottom.el, null);
32317             
32318             item.topEl = top;
32319             item.bottomEl = bottom;
32320         }
32321         
32322         this.barItems.push(item);
32323         
32324         return item;
32325     },
32326     
32327     getActive : function()
32328     {
32329         var active = false;
32330         
32331         Roo.each(this.barItems, function(v){
32332             
32333             if (!v.isActive()) {
32334                 return;
32335             }
32336             
32337             active = v;
32338             return false;
32339             
32340         });
32341         
32342         return active;
32343     },
32344     
32345     setActiveItem : function(item)
32346     {
32347         var prev = false;
32348         
32349         Roo.each(this.barItems, function(v){
32350             if (v.rid == item.rid) {
32351                 return ;
32352             }
32353             
32354             if (v.isActive()) {
32355                 v.setActive(false);
32356                 prev = v;
32357             }
32358         });
32359
32360         item.setActive(true);
32361         
32362         this.fireEvent('changed', this, item, prev);
32363     },
32364     
32365     getBarItem: function(rid)
32366     {
32367         var ret = false;
32368         
32369         Roo.each(this.barItems, function(e) {
32370             if (e.rid != rid) {
32371                 return;
32372             }
32373             
32374             ret =  e;
32375             return false;
32376         });
32377         
32378         return ret;
32379     },
32380     
32381     indexOfItem : function(item)
32382     {
32383         var index = false;
32384         
32385         Roo.each(this.barItems, function(v, i){
32386             
32387             if (v.rid != item.rid) {
32388                 return;
32389             }
32390             
32391             index = i;
32392             return false
32393         });
32394         
32395         return index;
32396     },
32397     
32398     setActiveNext : function()
32399     {
32400         var i = this.indexOfItem(this.getActive());
32401         
32402         if (i > this.barItems.length) {
32403             return;
32404         }
32405         
32406         this.setActiveItem(this.barItems[i+1]);
32407     },
32408     
32409     setActivePrev : function()
32410     {
32411         var i = this.indexOfItem(this.getActive());
32412         
32413         if (i  < 1) {
32414             return;
32415         }
32416         
32417         this.setActiveItem(this.barItems[i-1]);
32418     },
32419     
32420     format : function()
32421     {
32422         if(!this.barItems.length){
32423             return;
32424         }
32425      
32426         var width = 100 / this.barItems.length;
32427         
32428         Roo.each(this.barItems, function(i){
32429             i.el.setStyle('width', width + '%');
32430             i.topEl.el.setStyle('width', width + '%');
32431             i.bottomEl.el.setStyle('width', width + '%');
32432         }, this);
32433         
32434     }
32435     
32436 });
32437 /*
32438  * - LGPL
32439  *
32440  * Nav Progress Item
32441  * 
32442  */
32443
32444 /**
32445  * @class Roo.bootstrap.NavProgressItem
32446  * @extends Roo.bootstrap.Component
32447  * Bootstrap NavProgressItem class
32448  * @cfg {String} rid the reference id
32449  * @cfg {Boolean} active (true|false) Is item active default false
32450  * @cfg {Boolean} disabled (true|false) Is item active default false
32451  * @cfg {String} html
32452  * @cfg {String} position (top|bottom) text position default bottom
32453  * @cfg {String} icon show icon instead of number
32454  * 
32455  * @constructor
32456  * Create a new NavProgressItem
32457  * @param {Object} config The config object
32458  */
32459 Roo.bootstrap.NavProgressItem = function(config){
32460     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32461     this.addEvents({
32462         // raw events
32463         /**
32464          * @event click
32465          * The raw click event for the entire grid.
32466          * @param {Roo.bootstrap.NavProgressItem} this
32467          * @param {Roo.EventObject} e
32468          */
32469         "click" : true
32470     });
32471    
32472 };
32473
32474 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32475     
32476     rid : '',
32477     active : false,
32478     disabled : false,
32479     html : '',
32480     position : 'bottom',
32481     icon : false,
32482     
32483     getAutoCreate : function()
32484     {
32485         var iconCls = 'roo-navigation-bar-item-icon';
32486         
32487         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32488         
32489         var cfg = {
32490             tag: 'li',
32491             cls: 'roo-navigation-bar-item',
32492             cn : [
32493                 {
32494                     tag : 'i',
32495                     cls : iconCls
32496                 }
32497             ]
32498         };
32499         
32500         if(this.active){
32501             cfg.cls += ' active';
32502         }
32503         if(this.disabled){
32504             cfg.cls += ' disabled';
32505         }
32506         
32507         return cfg;
32508     },
32509     
32510     disable : function()
32511     {
32512         this.setDisabled(true);
32513     },
32514     
32515     enable : function()
32516     {
32517         this.setDisabled(false);
32518     },
32519     
32520     initEvents: function() 
32521     {
32522         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32523         
32524         this.iconEl.on('click', this.onClick, this);
32525     },
32526     
32527     onClick : function(e)
32528     {
32529         e.preventDefault();
32530         
32531         if(this.disabled){
32532             return;
32533         }
32534         
32535         if(this.fireEvent('click', this, e) === false){
32536             return;
32537         };
32538         
32539         this.parent().setActiveItem(this);
32540     },
32541     
32542     isActive: function () 
32543     {
32544         return this.active;
32545     },
32546     
32547     setActive : function(state)
32548     {
32549         if(this.active == state){
32550             return;
32551         }
32552         
32553         this.active = state;
32554         
32555         if (state) {
32556             this.el.addClass('active');
32557             return;
32558         }
32559         
32560         this.el.removeClass('active');
32561         
32562         return;
32563     },
32564     
32565     setDisabled : function(state)
32566     {
32567         if(this.disabled == state){
32568             return;
32569         }
32570         
32571         this.disabled = state;
32572         
32573         if (state) {
32574             this.el.addClass('disabled');
32575             return;
32576         }
32577         
32578         this.el.removeClass('disabled');
32579     },
32580     
32581     tooltipEl : function()
32582     {
32583         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32584     }
32585 });
32586  
32587
32588  /*
32589  * - LGPL
32590  *
32591  * FieldLabel
32592  * 
32593  */
32594
32595 /**
32596  * @class Roo.bootstrap.FieldLabel
32597  * @extends Roo.bootstrap.Component
32598  * Bootstrap FieldLabel class
32599  * @cfg {String} html contents of the element
32600  * @cfg {String} tag tag of the element default label
32601  * @cfg {String} cls class of the element
32602  * @cfg {String} target label target 
32603  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32604  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32605  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32606  * @cfg {String} iconTooltip default "This field is required"
32607  * @cfg {String} indicatorpos (left|right) default left
32608  * 
32609  * @constructor
32610  * Create a new FieldLabel
32611  * @param {Object} config The config object
32612  */
32613
32614 Roo.bootstrap.FieldLabel = function(config){
32615     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32616     
32617     this.addEvents({
32618             /**
32619              * @event invalid
32620              * Fires after the field has been marked as invalid.
32621              * @param {Roo.form.FieldLabel} this
32622              * @param {String} msg The validation message
32623              */
32624             invalid : true,
32625             /**
32626              * @event valid
32627              * Fires after the field has been validated with no errors.
32628              * @param {Roo.form.FieldLabel} this
32629              */
32630             valid : true
32631         });
32632 };
32633
32634 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32635     
32636     tag: 'label',
32637     cls: '',
32638     html: '',
32639     target: '',
32640     allowBlank : true,
32641     invalidClass : 'has-warning',
32642     validClass : 'has-success',
32643     iconTooltip : 'This field is required',
32644     indicatorpos : 'left',
32645     
32646     getAutoCreate : function(){
32647         
32648         var cls = "";
32649         if (!this.allowBlank) {
32650             cls  = "visible";
32651         }
32652         
32653         var cfg = {
32654             tag : this.tag,
32655             cls : 'roo-bootstrap-field-label ' + this.cls,
32656             for : this.target,
32657             cn : [
32658                 {
32659                     tag : 'i',
32660                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32661                     tooltip : this.iconTooltip
32662                 },
32663                 {
32664                     tag : 'span',
32665                     html : this.html
32666                 }
32667             ] 
32668         };
32669         
32670         if(this.indicatorpos == 'right'){
32671             var cfg = {
32672                 tag : this.tag,
32673                 cls : 'roo-bootstrap-field-label ' + this.cls,
32674                 for : this.target,
32675                 cn : [
32676                     {
32677                         tag : 'span',
32678                         html : this.html
32679                     },
32680                     {
32681                         tag : 'i',
32682                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32683                         tooltip : this.iconTooltip
32684                     }
32685                 ] 
32686             };
32687         }
32688         
32689         return cfg;
32690     },
32691     
32692     initEvents: function() 
32693     {
32694         Roo.bootstrap.Element.superclass.initEvents.call(this);
32695         
32696         this.indicator = this.indicatorEl();
32697         
32698         if(this.indicator){
32699             this.indicator.removeClass('visible');
32700             this.indicator.addClass('invisible');
32701         }
32702         
32703         Roo.bootstrap.FieldLabel.register(this);
32704     },
32705     
32706     indicatorEl : function()
32707     {
32708         var indicator = this.el.select('i.roo-required-indicator',true).first();
32709         
32710         if(!indicator){
32711             return false;
32712         }
32713         
32714         return indicator;
32715         
32716     },
32717     
32718     /**
32719      * Mark this field as valid
32720      */
32721     markValid : function()
32722     {
32723         if(this.indicator){
32724             this.indicator.removeClass('visible');
32725             this.indicator.addClass('invisible');
32726         }
32727         if (Roo.bootstrap.version == 3) {
32728             this.el.removeClass(this.invalidClass);
32729             this.el.addClass(this.validClass);
32730         } else {
32731             this.el.removeClass('is-invalid');
32732             this.el.addClass('is-valid');
32733         }
32734         
32735         
32736         this.fireEvent('valid', this);
32737     },
32738     
32739     /**
32740      * Mark this field as invalid
32741      * @param {String} msg The validation message
32742      */
32743     markInvalid : function(msg)
32744     {
32745         if(this.indicator){
32746             this.indicator.removeClass('invisible');
32747             this.indicator.addClass('visible');
32748         }
32749           if (Roo.bootstrap.version == 3) {
32750             this.el.removeClass(this.validClass);
32751             this.el.addClass(this.invalidClass);
32752         } else {
32753             this.el.removeClass('is-valid');
32754             this.el.addClass('is-invalid');
32755         }
32756         
32757         
32758         this.fireEvent('invalid', this, msg);
32759     }
32760     
32761    
32762 });
32763
32764 Roo.apply(Roo.bootstrap.FieldLabel, {
32765     
32766     groups: {},
32767     
32768      /**
32769     * register a FieldLabel Group
32770     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32771     */
32772     register : function(label)
32773     {
32774         if(this.groups.hasOwnProperty(label.target)){
32775             return;
32776         }
32777      
32778         this.groups[label.target] = label;
32779         
32780     },
32781     /**
32782     * fetch a FieldLabel Group based on the target
32783     * @param {string} target
32784     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32785     */
32786     get: function(target) {
32787         if (typeof(this.groups[target]) == 'undefined') {
32788             return false;
32789         }
32790         
32791         return this.groups[target] ;
32792     }
32793 });
32794
32795  
32796
32797  /*
32798  * - LGPL
32799  *
32800  * page DateSplitField.
32801  * 
32802  */
32803
32804
32805 /**
32806  * @class Roo.bootstrap.DateSplitField
32807  * @extends Roo.bootstrap.Component
32808  * Bootstrap DateSplitField class
32809  * @cfg {string} fieldLabel - the label associated
32810  * @cfg {Number} labelWidth set the width of label (0-12)
32811  * @cfg {String} labelAlign (top|left)
32812  * @cfg {Boolean} dayAllowBlank (true|false) default false
32813  * @cfg {Boolean} monthAllowBlank (true|false) default false
32814  * @cfg {Boolean} yearAllowBlank (true|false) default false
32815  * @cfg {string} dayPlaceholder 
32816  * @cfg {string} monthPlaceholder
32817  * @cfg {string} yearPlaceholder
32818  * @cfg {string} dayFormat default 'd'
32819  * @cfg {string} monthFormat default 'm'
32820  * @cfg {string} yearFormat default 'Y'
32821  * @cfg {Number} labellg set the width of label (1-12)
32822  * @cfg {Number} labelmd set the width of label (1-12)
32823  * @cfg {Number} labelsm set the width of label (1-12)
32824  * @cfg {Number} labelxs set the width of label (1-12)
32825
32826  *     
32827  * @constructor
32828  * Create a new DateSplitField
32829  * @param {Object} config The config object
32830  */
32831
32832 Roo.bootstrap.DateSplitField = function(config){
32833     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32834     
32835     this.addEvents({
32836         // raw events
32837          /**
32838          * @event years
32839          * getting the data of years
32840          * @param {Roo.bootstrap.DateSplitField} this
32841          * @param {Object} years
32842          */
32843         "years" : true,
32844         /**
32845          * @event days
32846          * getting the data of days
32847          * @param {Roo.bootstrap.DateSplitField} this
32848          * @param {Object} days
32849          */
32850         "days" : true,
32851         /**
32852          * @event invalid
32853          * Fires after the field has been marked as invalid.
32854          * @param {Roo.form.Field} this
32855          * @param {String} msg The validation message
32856          */
32857         invalid : true,
32858        /**
32859          * @event valid
32860          * Fires after the field has been validated with no errors.
32861          * @param {Roo.form.Field} this
32862          */
32863         valid : true
32864     });
32865 };
32866
32867 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32868     
32869     fieldLabel : '',
32870     labelAlign : 'top',
32871     labelWidth : 3,
32872     dayAllowBlank : false,
32873     monthAllowBlank : false,
32874     yearAllowBlank : false,
32875     dayPlaceholder : '',
32876     monthPlaceholder : '',
32877     yearPlaceholder : '',
32878     dayFormat : 'd',
32879     monthFormat : 'm',
32880     yearFormat : 'Y',
32881     isFormField : true,
32882     labellg : 0,
32883     labelmd : 0,
32884     labelsm : 0,
32885     labelxs : 0,
32886     
32887     getAutoCreate : function()
32888     {
32889         var cfg = {
32890             tag : 'div',
32891             cls : 'row roo-date-split-field-group',
32892             cn : [
32893                 {
32894                     tag : 'input',
32895                     type : 'hidden',
32896                     cls : 'form-hidden-field roo-date-split-field-group-value',
32897                     name : this.name
32898                 }
32899             ]
32900         };
32901         
32902         var labelCls = 'col-md-12';
32903         var contentCls = 'col-md-4';
32904         
32905         if(this.fieldLabel){
32906             
32907             var label = {
32908                 tag : 'div',
32909                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32910                 cn : [
32911                     {
32912                         tag : 'label',
32913                         html : this.fieldLabel
32914                     }
32915                 ]
32916             };
32917             
32918             if(this.labelAlign == 'left'){
32919             
32920                 if(this.labelWidth > 12){
32921                     label.style = "width: " + this.labelWidth + 'px';
32922                 }
32923
32924                 if(this.labelWidth < 13 && this.labelmd == 0){
32925                     this.labelmd = this.labelWidth;
32926                 }
32927
32928                 if(this.labellg > 0){
32929                     labelCls = ' col-lg-' + this.labellg;
32930                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32931                 }
32932
32933                 if(this.labelmd > 0){
32934                     labelCls = ' col-md-' + this.labelmd;
32935                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32936                 }
32937
32938                 if(this.labelsm > 0){
32939                     labelCls = ' col-sm-' + this.labelsm;
32940                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32941                 }
32942
32943                 if(this.labelxs > 0){
32944                     labelCls = ' col-xs-' + this.labelxs;
32945                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32946                 }
32947             }
32948             
32949             label.cls += ' ' + labelCls;
32950             
32951             cfg.cn.push(label);
32952         }
32953         
32954         Roo.each(['day', 'month', 'year'], function(t){
32955             cfg.cn.push({
32956                 tag : 'div',
32957                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32958             });
32959         }, this);
32960         
32961         return cfg;
32962     },
32963     
32964     inputEl: function ()
32965     {
32966         return this.el.select('.roo-date-split-field-group-value', true).first();
32967     },
32968     
32969     onRender : function(ct, position) 
32970     {
32971         var _this = this;
32972         
32973         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32974         
32975         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32976         
32977         this.dayField = new Roo.bootstrap.ComboBox({
32978             allowBlank : this.dayAllowBlank,
32979             alwaysQuery : true,
32980             displayField : 'value',
32981             editable : false,
32982             fieldLabel : '',
32983             forceSelection : true,
32984             mode : 'local',
32985             placeholder : this.dayPlaceholder,
32986             selectOnFocus : true,
32987             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32988             triggerAction : 'all',
32989             typeAhead : true,
32990             valueField : 'value',
32991             store : new Roo.data.SimpleStore({
32992                 data : (function() {    
32993                     var days = [];
32994                     _this.fireEvent('days', _this, days);
32995                     return days;
32996                 })(),
32997                 fields : [ 'value' ]
32998             }),
32999             listeners : {
33000                 select : function (_self, record, index)
33001                 {
33002                     _this.setValue(_this.getValue());
33003                 }
33004             }
33005         });
33006
33007         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33008         
33009         this.monthField = new Roo.bootstrap.MonthField({
33010             after : '<i class=\"fa fa-calendar\"></i>',
33011             allowBlank : this.monthAllowBlank,
33012             placeholder : this.monthPlaceholder,
33013             readOnly : true,
33014             listeners : {
33015                 render : function (_self)
33016                 {
33017                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33018                         e.preventDefault();
33019                         _self.focus();
33020                     });
33021                 },
33022                 select : function (_self, oldvalue, newvalue)
33023                 {
33024                     _this.setValue(_this.getValue());
33025                 }
33026             }
33027         });
33028         
33029         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33030         
33031         this.yearField = new Roo.bootstrap.ComboBox({
33032             allowBlank : this.yearAllowBlank,
33033             alwaysQuery : true,
33034             displayField : 'value',
33035             editable : false,
33036             fieldLabel : '',
33037             forceSelection : true,
33038             mode : 'local',
33039             placeholder : this.yearPlaceholder,
33040             selectOnFocus : true,
33041             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33042             triggerAction : 'all',
33043             typeAhead : true,
33044             valueField : 'value',
33045             store : new Roo.data.SimpleStore({
33046                 data : (function() {
33047                     var years = [];
33048                     _this.fireEvent('years', _this, years);
33049                     return years;
33050                 })(),
33051                 fields : [ 'value' ]
33052             }),
33053             listeners : {
33054                 select : function (_self, record, index)
33055                 {
33056                     _this.setValue(_this.getValue());
33057                 }
33058             }
33059         });
33060
33061         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33062     },
33063     
33064     setValue : function(v, format)
33065     {
33066         this.inputEl.dom.value = v;
33067         
33068         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33069         
33070         var d = Date.parseDate(v, f);
33071         
33072         if(!d){
33073             this.validate();
33074             return;
33075         }
33076         
33077         this.setDay(d.format(this.dayFormat));
33078         this.setMonth(d.format(this.monthFormat));
33079         this.setYear(d.format(this.yearFormat));
33080         
33081         this.validate();
33082         
33083         return;
33084     },
33085     
33086     setDay : function(v)
33087     {
33088         this.dayField.setValue(v);
33089         this.inputEl.dom.value = this.getValue();
33090         this.validate();
33091         return;
33092     },
33093     
33094     setMonth : function(v)
33095     {
33096         this.monthField.setValue(v, true);
33097         this.inputEl.dom.value = this.getValue();
33098         this.validate();
33099         return;
33100     },
33101     
33102     setYear : function(v)
33103     {
33104         this.yearField.setValue(v);
33105         this.inputEl.dom.value = this.getValue();
33106         this.validate();
33107         return;
33108     },
33109     
33110     getDay : function()
33111     {
33112         return this.dayField.getValue();
33113     },
33114     
33115     getMonth : function()
33116     {
33117         return this.monthField.getValue();
33118     },
33119     
33120     getYear : function()
33121     {
33122         return this.yearField.getValue();
33123     },
33124     
33125     getValue : function()
33126     {
33127         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33128         
33129         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33130         
33131         return date;
33132     },
33133     
33134     reset : function()
33135     {
33136         this.setDay('');
33137         this.setMonth('');
33138         this.setYear('');
33139         this.inputEl.dom.value = '';
33140         this.validate();
33141         return;
33142     },
33143     
33144     validate : function()
33145     {
33146         var d = this.dayField.validate();
33147         var m = this.monthField.validate();
33148         var y = this.yearField.validate();
33149         
33150         var valid = true;
33151         
33152         if(
33153                 (!this.dayAllowBlank && !d) ||
33154                 (!this.monthAllowBlank && !m) ||
33155                 (!this.yearAllowBlank && !y)
33156         ){
33157             valid = false;
33158         }
33159         
33160         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33161             return valid;
33162         }
33163         
33164         if(valid){
33165             this.markValid();
33166             return valid;
33167         }
33168         
33169         this.markInvalid();
33170         
33171         return valid;
33172     },
33173     
33174     markValid : function()
33175     {
33176         
33177         var label = this.el.select('label', true).first();
33178         var icon = this.el.select('i.fa-star', true).first();
33179
33180         if(label && icon){
33181             icon.remove();
33182         }
33183         
33184         this.fireEvent('valid', this);
33185     },
33186     
33187      /**
33188      * Mark this field as invalid
33189      * @param {String} msg The validation message
33190      */
33191     markInvalid : function(msg)
33192     {
33193         
33194         var label = this.el.select('label', true).first();
33195         var icon = this.el.select('i.fa-star', true).first();
33196
33197         if(label && !icon){
33198             this.el.select('.roo-date-split-field-label', true).createChild({
33199                 tag : 'i',
33200                 cls : 'text-danger fa fa-lg fa-star',
33201                 tooltip : 'This field is required',
33202                 style : 'margin-right:5px;'
33203             }, label, true);
33204         }
33205         
33206         this.fireEvent('invalid', this, msg);
33207     },
33208     
33209     clearInvalid : function()
33210     {
33211         var label = this.el.select('label', true).first();
33212         var icon = this.el.select('i.fa-star', true).first();
33213
33214         if(label && icon){
33215             icon.remove();
33216         }
33217         
33218         this.fireEvent('valid', this);
33219     },
33220     
33221     getName: function()
33222     {
33223         return this.name;
33224     }
33225     
33226 });
33227
33228  /**
33229  *
33230  * This is based on 
33231  * http://masonry.desandro.com
33232  *
33233  * The idea is to render all the bricks based on vertical width...
33234  *
33235  * The original code extends 'outlayer' - we might need to use that....
33236  * 
33237  */
33238
33239
33240 /**
33241  * @class Roo.bootstrap.LayoutMasonry
33242  * @extends Roo.bootstrap.Component
33243  * Bootstrap Layout Masonry class
33244  * 
33245  * @constructor
33246  * Create a new Element
33247  * @param {Object} config The config object
33248  */
33249
33250 Roo.bootstrap.LayoutMasonry = function(config){
33251     
33252     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33253     
33254     this.bricks = [];
33255     
33256     Roo.bootstrap.LayoutMasonry.register(this);
33257     
33258     this.addEvents({
33259         // raw events
33260         /**
33261          * @event layout
33262          * Fire after layout the items
33263          * @param {Roo.bootstrap.LayoutMasonry} this
33264          * @param {Roo.EventObject} e
33265          */
33266         "layout" : true
33267     });
33268     
33269 };
33270
33271 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33272     
33273     /**
33274      * @cfg {Boolean} isLayoutInstant = no animation?
33275      */   
33276     isLayoutInstant : false, // needed?
33277    
33278     /**
33279      * @cfg {Number} boxWidth  width of the columns
33280      */   
33281     boxWidth : 450,
33282     
33283       /**
33284      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33285      */   
33286     boxHeight : 0,
33287     
33288     /**
33289      * @cfg {Number} padWidth padding below box..
33290      */   
33291     padWidth : 10, 
33292     
33293     /**
33294      * @cfg {Number} gutter gutter width..
33295      */   
33296     gutter : 10,
33297     
33298      /**
33299      * @cfg {Number} maxCols maximum number of columns
33300      */   
33301     
33302     maxCols: 0,
33303     
33304     /**
33305      * @cfg {Boolean} isAutoInitial defalut true
33306      */   
33307     isAutoInitial : true, 
33308     
33309     containerWidth: 0,
33310     
33311     /**
33312      * @cfg {Boolean} isHorizontal defalut false
33313      */   
33314     isHorizontal : false, 
33315
33316     currentSize : null,
33317     
33318     tag: 'div',
33319     
33320     cls: '',
33321     
33322     bricks: null, //CompositeElement
33323     
33324     cols : 1,
33325     
33326     _isLayoutInited : false,
33327     
33328 //    isAlternative : false, // only use for vertical layout...
33329     
33330     /**
33331      * @cfg {Number} alternativePadWidth padding below box..
33332      */   
33333     alternativePadWidth : 50,
33334     
33335     selectedBrick : [],
33336     
33337     getAutoCreate : function(){
33338         
33339         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33340         
33341         var cfg = {
33342             tag: this.tag,
33343             cls: 'blog-masonary-wrapper ' + this.cls,
33344             cn : {
33345                 cls : 'mas-boxes masonary'
33346             }
33347         };
33348         
33349         return cfg;
33350     },
33351     
33352     getChildContainer: function( )
33353     {
33354         if (this.boxesEl) {
33355             return this.boxesEl;
33356         }
33357         
33358         this.boxesEl = this.el.select('.mas-boxes').first();
33359         
33360         return this.boxesEl;
33361     },
33362     
33363     
33364     initEvents : function()
33365     {
33366         var _this = this;
33367         
33368         if(this.isAutoInitial){
33369             Roo.log('hook children rendered');
33370             this.on('childrenrendered', function() {
33371                 Roo.log('children rendered');
33372                 _this.initial();
33373             } ,this);
33374         }
33375     },
33376     
33377     initial : function()
33378     {
33379         this.selectedBrick = [];
33380         
33381         this.currentSize = this.el.getBox(true);
33382         
33383         Roo.EventManager.onWindowResize(this.resize, this); 
33384
33385         if(!this.isAutoInitial){
33386             this.layout();
33387             return;
33388         }
33389         
33390         this.layout();
33391         
33392         return;
33393         //this.layout.defer(500,this);
33394         
33395     },
33396     
33397     resize : function()
33398     {
33399         var cs = this.el.getBox(true);
33400         
33401         if (
33402                 this.currentSize.width == cs.width && 
33403                 this.currentSize.x == cs.x && 
33404                 this.currentSize.height == cs.height && 
33405                 this.currentSize.y == cs.y 
33406         ) {
33407             Roo.log("no change in with or X or Y");
33408             return;
33409         }
33410         
33411         this.currentSize = cs;
33412         
33413         this.layout();
33414         
33415     },
33416     
33417     layout : function()
33418     {   
33419         this._resetLayout();
33420         
33421         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33422         
33423         this.layoutItems( isInstant );
33424       
33425         this._isLayoutInited = true;
33426         
33427         this.fireEvent('layout', this);
33428         
33429     },
33430     
33431     _resetLayout : function()
33432     {
33433         if(this.isHorizontal){
33434             this.horizontalMeasureColumns();
33435             return;
33436         }
33437         
33438         this.verticalMeasureColumns();
33439         
33440     },
33441     
33442     verticalMeasureColumns : function()
33443     {
33444         this.getContainerWidth();
33445         
33446 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33447 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33448 //            return;
33449 //        }
33450         
33451         var boxWidth = this.boxWidth + this.padWidth;
33452         
33453         if(this.containerWidth < this.boxWidth){
33454             boxWidth = this.containerWidth
33455         }
33456         
33457         var containerWidth = this.containerWidth;
33458         
33459         var cols = Math.floor(containerWidth / boxWidth);
33460         
33461         this.cols = Math.max( cols, 1 );
33462         
33463         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33464         
33465         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33466         
33467         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33468         
33469         this.colWidth = boxWidth + avail - this.padWidth;
33470         
33471         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33472         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33473     },
33474     
33475     horizontalMeasureColumns : function()
33476     {
33477         this.getContainerWidth();
33478         
33479         var boxWidth = this.boxWidth;
33480         
33481         if(this.containerWidth < boxWidth){
33482             boxWidth = this.containerWidth;
33483         }
33484         
33485         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33486         
33487         this.el.setHeight(boxWidth);
33488         
33489     },
33490     
33491     getContainerWidth : function()
33492     {
33493         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33494     },
33495     
33496     layoutItems : function( isInstant )
33497     {
33498         Roo.log(this.bricks);
33499         
33500         var items = Roo.apply([], this.bricks);
33501         
33502         if(this.isHorizontal){
33503             this._horizontalLayoutItems( items , isInstant );
33504             return;
33505         }
33506         
33507 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33508 //            this._verticalAlternativeLayoutItems( items , isInstant );
33509 //            return;
33510 //        }
33511         
33512         this._verticalLayoutItems( items , isInstant );
33513         
33514     },
33515     
33516     _verticalLayoutItems : function ( items , isInstant)
33517     {
33518         if ( !items || !items.length ) {
33519             return;
33520         }
33521         
33522         var standard = [
33523             ['xs', 'xs', 'xs', 'tall'],
33524             ['xs', 'xs', 'tall'],
33525             ['xs', 'xs', 'sm'],
33526             ['xs', 'xs', 'xs'],
33527             ['xs', 'tall'],
33528             ['xs', 'sm'],
33529             ['xs', 'xs'],
33530             ['xs'],
33531             
33532             ['sm', 'xs', 'xs'],
33533             ['sm', 'xs'],
33534             ['sm'],
33535             
33536             ['tall', 'xs', 'xs', 'xs'],
33537             ['tall', 'xs', 'xs'],
33538             ['tall', 'xs'],
33539             ['tall']
33540             
33541         ];
33542         
33543         var queue = [];
33544         
33545         var boxes = [];
33546         
33547         var box = [];
33548         
33549         Roo.each(items, function(item, k){
33550             
33551             switch (item.size) {
33552                 // these layouts take up a full box,
33553                 case 'md' :
33554                 case 'md-left' :
33555                 case 'md-right' :
33556                 case 'wide' :
33557                     
33558                     if(box.length){
33559                         boxes.push(box);
33560                         box = [];
33561                     }
33562                     
33563                     boxes.push([item]);
33564                     
33565                     break;
33566                     
33567                 case 'xs' :
33568                 case 'sm' :
33569                 case 'tall' :
33570                     
33571                     box.push(item);
33572                     
33573                     break;
33574                 default :
33575                     break;
33576                     
33577             }
33578             
33579         }, this);
33580         
33581         if(box.length){
33582             boxes.push(box);
33583             box = [];
33584         }
33585         
33586         var filterPattern = function(box, length)
33587         {
33588             if(!box.length){
33589                 return;
33590             }
33591             
33592             var match = false;
33593             
33594             var pattern = box.slice(0, length);
33595             
33596             var format = [];
33597             
33598             Roo.each(pattern, function(i){
33599                 format.push(i.size);
33600             }, this);
33601             
33602             Roo.each(standard, function(s){
33603                 
33604                 if(String(s) != String(format)){
33605                     return;
33606                 }
33607                 
33608                 match = true;
33609                 return false;
33610                 
33611             }, this);
33612             
33613             if(!match && length == 1){
33614                 return;
33615             }
33616             
33617             if(!match){
33618                 filterPattern(box, length - 1);
33619                 return;
33620             }
33621                 
33622             queue.push(pattern);
33623
33624             box = box.slice(length, box.length);
33625
33626             filterPattern(box, 4);
33627
33628             return;
33629             
33630         }
33631         
33632         Roo.each(boxes, function(box, k){
33633             
33634             if(!box.length){
33635                 return;
33636             }
33637             
33638             if(box.length == 1){
33639                 queue.push(box);
33640                 return;
33641             }
33642             
33643             filterPattern(box, 4);
33644             
33645         }, this);
33646         
33647         this._processVerticalLayoutQueue( queue, isInstant );
33648         
33649     },
33650     
33651 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33652 //    {
33653 //        if ( !items || !items.length ) {
33654 //            return;
33655 //        }
33656 //
33657 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33658 //        
33659 //    },
33660     
33661     _horizontalLayoutItems : function ( items , isInstant)
33662     {
33663         if ( !items || !items.length || items.length < 3) {
33664             return;
33665         }
33666         
33667         items.reverse();
33668         
33669         var eItems = items.slice(0, 3);
33670         
33671         items = items.slice(3, items.length);
33672         
33673         var standard = [
33674             ['xs', 'xs', 'xs', 'wide'],
33675             ['xs', 'xs', 'wide'],
33676             ['xs', 'xs', 'sm'],
33677             ['xs', 'xs', 'xs'],
33678             ['xs', 'wide'],
33679             ['xs', 'sm'],
33680             ['xs', 'xs'],
33681             ['xs'],
33682             
33683             ['sm', 'xs', 'xs'],
33684             ['sm', 'xs'],
33685             ['sm'],
33686             
33687             ['wide', 'xs', 'xs', 'xs'],
33688             ['wide', 'xs', 'xs'],
33689             ['wide', 'xs'],
33690             ['wide'],
33691             
33692             ['wide-thin']
33693         ];
33694         
33695         var queue = [];
33696         
33697         var boxes = [];
33698         
33699         var box = [];
33700         
33701         Roo.each(items, function(item, k){
33702             
33703             switch (item.size) {
33704                 case 'md' :
33705                 case 'md-left' :
33706                 case 'md-right' :
33707                 case 'tall' :
33708                     
33709                     if(box.length){
33710                         boxes.push(box);
33711                         box = [];
33712                     }
33713                     
33714                     boxes.push([item]);
33715                     
33716                     break;
33717                     
33718                 case 'xs' :
33719                 case 'sm' :
33720                 case 'wide' :
33721                 case 'wide-thin' :
33722                     
33723                     box.push(item);
33724                     
33725                     break;
33726                 default :
33727                     break;
33728                     
33729             }
33730             
33731         }, this);
33732         
33733         if(box.length){
33734             boxes.push(box);
33735             box = [];
33736         }
33737         
33738         var filterPattern = function(box, length)
33739         {
33740             if(!box.length){
33741                 return;
33742             }
33743             
33744             var match = false;
33745             
33746             var pattern = box.slice(0, length);
33747             
33748             var format = [];
33749             
33750             Roo.each(pattern, function(i){
33751                 format.push(i.size);
33752             }, this);
33753             
33754             Roo.each(standard, function(s){
33755                 
33756                 if(String(s) != String(format)){
33757                     return;
33758                 }
33759                 
33760                 match = true;
33761                 return false;
33762                 
33763             }, this);
33764             
33765             if(!match && length == 1){
33766                 return;
33767             }
33768             
33769             if(!match){
33770                 filterPattern(box, length - 1);
33771                 return;
33772             }
33773                 
33774             queue.push(pattern);
33775
33776             box = box.slice(length, box.length);
33777
33778             filterPattern(box, 4);
33779
33780             return;
33781             
33782         }
33783         
33784         Roo.each(boxes, function(box, k){
33785             
33786             if(!box.length){
33787                 return;
33788             }
33789             
33790             if(box.length == 1){
33791                 queue.push(box);
33792                 return;
33793             }
33794             
33795             filterPattern(box, 4);
33796             
33797         }, this);
33798         
33799         
33800         var prune = [];
33801         
33802         var pos = this.el.getBox(true);
33803         
33804         var minX = pos.x;
33805         
33806         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33807         
33808         var hit_end = false;
33809         
33810         Roo.each(queue, function(box){
33811             
33812             if(hit_end){
33813                 
33814                 Roo.each(box, function(b){
33815                 
33816                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33817                     b.el.hide();
33818
33819                 }, this);
33820
33821                 return;
33822             }
33823             
33824             var mx = 0;
33825             
33826             Roo.each(box, function(b){
33827                 
33828                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33829                 b.el.show();
33830
33831                 mx = Math.max(mx, b.x);
33832                 
33833             }, this);
33834             
33835             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33836             
33837             if(maxX < minX){
33838                 
33839                 Roo.each(box, function(b){
33840                 
33841                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33842                     b.el.hide();
33843                     
33844                 }, this);
33845                 
33846                 hit_end = true;
33847                 
33848                 return;
33849             }
33850             
33851             prune.push(box);
33852             
33853         }, this);
33854         
33855         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33856     },
33857     
33858     /** Sets position of item in DOM
33859     * @param {Element} item
33860     * @param {Number} x - horizontal position
33861     * @param {Number} y - vertical position
33862     * @param {Boolean} isInstant - disables transitions
33863     */
33864     _processVerticalLayoutQueue : function( queue, isInstant )
33865     {
33866         var pos = this.el.getBox(true);
33867         var x = pos.x;
33868         var y = pos.y;
33869         var maxY = [];
33870         
33871         for (var i = 0; i < this.cols; i++){
33872             maxY[i] = pos.y;
33873         }
33874         
33875         Roo.each(queue, function(box, k){
33876             
33877             var col = k % this.cols;
33878             
33879             Roo.each(box, function(b,kk){
33880                 
33881                 b.el.position('absolute');
33882                 
33883                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33884                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33885                 
33886                 if(b.size == 'md-left' || b.size == 'md-right'){
33887                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33888                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33889                 }
33890                 
33891                 b.el.setWidth(width);
33892                 b.el.setHeight(height);
33893                 // iframe?
33894                 b.el.select('iframe',true).setSize(width,height);
33895                 
33896             }, this);
33897             
33898             for (var i = 0; i < this.cols; i++){
33899                 
33900                 if(maxY[i] < maxY[col]){
33901                     col = i;
33902                     continue;
33903                 }
33904                 
33905                 col = Math.min(col, i);
33906                 
33907             }
33908             
33909             x = pos.x + col * (this.colWidth + this.padWidth);
33910             
33911             y = maxY[col];
33912             
33913             var positions = [];
33914             
33915             switch (box.length){
33916                 case 1 :
33917                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33918                     break;
33919                 case 2 :
33920                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33921                     break;
33922                 case 3 :
33923                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33924                     break;
33925                 case 4 :
33926                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33927                     break;
33928                 default :
33929                     break;
33930             }
33931             
33932             Roo.each(box, function(b,kk){
33933                 
33934                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33935                 
33936                 var sz = b.el.getSize();
33937                 
33938                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33939                 
33940             }, this);
33941             
33942         }, this);
33943         
33944         var mY = 0;
33945         
33946         for (var i = 0; i < this.cols; i++){
33947             mY = Math.max(mY, maxY[i]);
33948         }
33949         
33950         this.el.setHeight(mY - pos.y);
33951         
33952     },
33953     
33954 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33955 //    {
33956 //        var pos = this.el.getBox(true);
33957 //        var x = pos.x;
33958 //        var y = pos.y;
33959 //        var maxX = pos.right;
33960 //        
33961 //        var maxHeight = 0;
33962 //        
33963 //        Roo.each(items, function(item, k){
33964 //            
33965 //            var c = k % 2;
33966 //            
33967 //            item.el.position('absolute');
33968 //                
33969 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33970 //
33971 //            item.el.setWidth(width);
33972 //
33973 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33974 //
33975 //            item.el.setHeight(height);
33976 //            
33977 //            if(c == 0){
33978 //                item.el.setXY([x, y], isInstant ? false : true);
33979 //            } else {
33980 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33981 //            }
33982 //            
33983 //            y = y + height + this.alternativePadWidth;
33984 //            
33985 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33986 //            
33987 //        }, this);
33988 //        
33989 //        this.el.setHeight(maxHeight);
33990 //        
33991 //    },
33992     
33993     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33994     {
33995         var pos = this.el.getBox(true);
33996         
33997         var minX = pos.x;
33998         var minY = pos.y;
33999         
34000         var maxX = pos.right;
34001         
34002         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34003         
34004         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34005         
34006         Roo.each(queue, function(box, k){
34007             
34008             Roo.each(box, function(b, kk){
34009                 
34010                 b.el.position('absolute');
34011                 
34012                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34013                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34014                 
34015                 if(b.size == 'md-left' || b.size == 'md-right'){
34016                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34017                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34018                 }
34019                 
34020                 b.el.setWidth(width);
34021                 b.el.setHeight(height);
34022                 
34023             }, this);
34024             
34025             if(!box.length){
34026                 return;
34027             }
34028             
34029             var positions = [];
34030             
34031             switch (box.length){
34032                 case 1 :
34033                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34034                     break;
34035                 case 2 :
34036                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34037                     break;
34038                 case 3 :
34039                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34040                     break;
34041                 case 4 :
34042                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34043                     break;
34044                 default :
34045                     break;
34046             }
34047             
34048             Roo.each(box, function(b,kk){
34049                 
34050                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34051                 
34052                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34053                 
34054             }, this);
34055             
34056         }, this);
34057         
34058     },
34059     
34060     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34061     {
34062         Roo.each(eItems, function(b,k){
34063             
34064             b.size = (k == 0) ? 'sm' : 'xs';
34065             b.x = (k == 0) ? 2 : 1;
34066             b.y = (k == 0) ? 2 : 1;
34067             
34068             b.el.position('absolute');
34069             
34070             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34071                 
34072             b.el.setWidth(width);
34073             
34074             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34075             
34076             b.el.setHeight(height);
34077             
34078         }, this);
34079
34080         var positions = [];
34081         
34082         positions.push({
34083             x : maxX - this.unitWidth * 2 - this.gutter,
34084             y : minY
34085         });
34086         
34087         positions.push({
34088             x : maxX - this.unitWidth,
34089             y : minY + (this.unitWidth + this.gutter) * 2
34090         });
34091         
34092         positions.push({
34093             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34094             y : minY
34095         });
34096         
34097         Roo.each(eItems, function(b,k){
34098             
34099             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34100
34101         }, this);
34102         
34103     },
34104     
34105     getVerticalOneBoxColPositions : function(x, y, box)
34106     {
34107         var pos = [];
34108         
34109         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34110         
34111         if(box[0].size == 'md-left'){
34112             rand = 0;
34113         }
34114         
34115         if(box[0].size == 'md-right'){
34116             rand = 1;
34117         }
34118         
34119         pos.push({
34120             x : x + (this.unitWidth + this.gutter) * rand,
34121             y : y
34122         });
34123         
34124         return pos;
34125     },
34126     
34127     getVerticalTwoBoxColPositions : function(x, y, box)
34128     {
34129         var pos = [];
34130         
34131         if(box[0].size == 'xs'){
34132             
34133             pos.push({
34134                 x : x,
34135                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34136             });
34137
34138             pos.push({
34139                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34140                 y : y
34141             });
34142             
34143             return pos;
34144             
34145         }
34146         
34147         pos.push({
34148             x : x,
34149             y : y
34150         });
34151
34152         pos.push({
34153             x : x + (this.unitWidth + this.gutter) * 2,
34154             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34155         });
34156         
34157         return pos;
34158         
34159     },
34160     
34161     getVerticalThreeBoxColPositions : function(x, y, box)
34162     {
34163         var pos = [];
34164         
34165         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34166             
34167             pos.push({
34168                 x : x,
34169                 y : y
34170             });
34171
34172             pos.push({
34173                 x : x + (this.unitWidth + this.gutter) * 1,
34174                 y : y
34175             });
34176             
34177             pos.push({
34178                 x : x + (this.unitWidth + this.gutter) * 2,
34179                 y : y
34180             });
34181             
34182             return pos;
34183             
34184         }
34185         
34186         if(box[0].size == 'xs' && box[1].size == 'xs'){
34187             
34188             pos.push({
34189                 x : x,
34190                 y : y
34191             });
34192
34193             pos.push({
34194                 x : x,
34195                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34196             });
34197             
34198             pos.push({
34199                 x : x + (this.unitWidth + this.gutter) * 1,
34200                 y : y
34201             });
34202             
34203             return pos;
34204             
34205         }
34206         
34207         pos.push({
34208             x : x,
34209             y : y
34210         });
34211
34212         pos.push({
34213             x : x + (this.unitWidth + this.gutter) * 2,
34214             y : y
34215         });
34216
34217         pos.push({
34218             x : x + (this.unitWidth + this.gutter) * 2,
34219             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34220         });
34221             
34222         return pos;
34223         
34224     },
34225     
34226     getVerticalFourBoxColPositions : function(x, y, box)
34227     {
34228         var pos = [];
34229         
34230         if(box[0].size == 'xs'){
34231             
34232             pos.push({
34233                 x : x,
34234                 y : y
34235             });
34236
34237             pos.push({
34238                 x : x,
34239                 y : y + (this.unitHeight + this.gutter) * 1
34240             });
34241             
34242             pos.push({
34243                 x : x,
34244                 y : y + (this.unitHeight + this.gutter) * 2
34245             });
34246             
34247             pos.push({
34248                 x : x + (this.unitWidth + this.gutter) * 1,
34249                 y : y
34250             });
34251             
34252             return pos;
34253             
34254         }
34255         
34256         pos.push({
34257             x : x,
34258             y : y
34259         });
34260
34261         pos.push({
34262             x : x + (this.unitWidth + this.gutter) * 2,
34263             y : y
34264         });
34265
34266         pos.push({
34267             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34268             y : y + (this.unitHeight + this.gutter) * 1
34269         });
34270
34271         pos.push({
34272             x : x + (this.unitWidth + this.gutter) * 2,
34273             y : y + (this.unitWidth + this.gutter) * 2
34274         });
34275
34276         return pos;
34277         
34278     },
34279     
34280     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34281     {
34282         var pos = [];
34283         
34284         if(box[0].size == 'md-left'){
34285             pos.push({
34286                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34287                 y : minY
34288             });
34289             
34290             return pos;
34291         }
34292         
34293         if(box[0].size == 'md-right'){
34294             pos.push({
34295                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34296                 y : minY + (this.unitWidth + this.gutter) * 1
34297             });
34298             
34299             return pos;
34300         }
34301         
34302         var rand = Math.floor(Math.random() * (4 - box[0].y));
34303         
34304         pos.push({
34305             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34306             y : minY + (this.unitWidth + this.gutter) * rand
34307         });
34308         
34309         return pos;
34310         
34311     },
34312     
34313     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34314     {
34315         var pos = [];
34316         
34317         if(box[0].size == 'xs'){
34318             
34319             pos.push({
34320                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34321                 y : minY
34322             });
34323
34324             pos.push({
34325                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34326                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34327             });
34328             
34329             return pos;
34330             
34331         }
34332         
34333         pos.push({
34334             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34335             y : minY
34336         });
34337
34338         pos.push({
34339             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34340             y : minY + (this.unitWidth + this.gutter) * 2
34341         });
34342         
34343         return pos;
34344         
34345     },
34346     
34347     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34348     {
34349         var pos = [];
34350         
34351         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34352             
34353             pos.push({
34354                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34355                 y : minY
34356             });
34357
34358             pos.push({
34359                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34360                 y : minY + (this.unitWidth + this.gutter) * 1
34361             });
34362             
34363             pos.push({
34364                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34365                 y : minY + (this.unitWidth + this.gutter) * 2
34366             });
34367             
34368             return pos;
34369             
34370         }
34371         
34372         if(box[0].size == 'xs' && box[1].size == 'xs'){
34373             
34374             pos.push({
34375                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34376                 y : minY
34377             });
34378
34379             pos.push({
34380                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34381                 y : minY
34382             });
34383             
34384             pos.push({
34385                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34386                 y : minY + (this.unitWidth + this.gutter) * 1
34387             });
34388             
34389             return pos;
34390             
34391         }
34392         
34393         pos.push({
34394             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34395             y : minY
34396         });
34397
34398         pos.push({
34399             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34400             y : minY + (this.unitWidth + this.gutter) * 2
34401         });
34402
34403         pos.push({
34404             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34405             y : minY + (this.unitWidth + this.gutter) * 2
34406         });
34407             
34408         return pos;
34409         
34410     },
34411     
34412     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34413     {
34414         var pos = [];
34415         
34416         if(box[0].size == 'xs'){
34417             
34418             pos.push({
34419                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34420                 y : minY
34421             });
34422
34423             pos.push({
34424                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34425                 y : minY
34426             });
34427             
34428             pos.push({
34429                 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),
34430                 y : minY
34431             });
34432             
34433             pos.push({
34434                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34435                 y : minY + (this.unitWidth + this.gutter) * 1
34436             });
34437             
34438             return pos;
34439             
34440         }
34441         
34442         pos.push({
34443             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34444             y : minY
34445         });
34446         
34447         pos.push({
34448             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34449             y : minY + (this.unitWidth + this.gutter) * 2
34450         });
34451         
34452         pos.push({
34453             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34454             y : minY + (this.unitWidth + this.gutter) * 2
34455         });
34456         
34457         pos.push({
34458             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),
34459             y : minY + (this.unitWidth + this.gutter) * 2
34460         });
34461
34462         return pos;
34463         
34464     },
34465     
34466     /**
34467     * remove a Masonry Brick
34468     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34469     */
34470     removeBrick : function(brick_id)
34471     {
34472         if (!brick_id) {
34473             return;
34474         }
34475         
34476         for (var i = 0; i<this.bricks.length; i++) {
34477             if (this.bricks[i].id == brick_id) {
34478                 this.bricks.splice(i,1);
34479                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34480                 this.initial();
34481             }
34482         }
34483     },
34484     
34485     /**
34486     * adds a Masonry Brick
34487     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34488     */
34489     addBrick : function(cfg)
34490     {
34491         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34492         //this.register(cn);
34493         cn.parentId = this.id;
34494         cn.render(this.el);
34495         return cn;
34496     },
34497     
34498     /**
34499     * register a Masonry Brick
34500     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34501     */
34502     
34503     register : function(brick)
34504     {
34505         this.bricks.push(brick);
34506         brick.masonryId = this.id;
34507     },
34508     
34509     /**
34510     * clear all the Masonry Brick
34511     */
34512     clearAll : function()
34513     {
34514         this.bricks = [];
34515         //this.getChildContainer().dom.innerHTML = "";
34516         this.el.dom.innerHTML = '';
34517     },
34518     
34519     getSelected : function()
34520     {
34521         if (!this.selectedBrick) {
34522             return false;
34523         }
34524         
34525         return this.selectedBrick;
34526     }
34527 });
34528
34529 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34530     
34531     groups: {},
34532      /**
34533     * register a Masonry Layout
34534     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34535     */
34536     
34537     register : function(layout)
34538     {
34539         this.groups[layout.id] = layout;
34540     },
34541     /**
34542     * fetch a  Masonry Layout based on the masonry layout ID
34543     * @param {string} the masonry layout to add
34544     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34545     */
34546     
34547     get: function(layout_id) {
34548         if (typeof(this.groups[layout_id]) == 'undefined') {
34549             return false;
34550         }
34551         return this.groups[layout_id] ;
34552     }
34553     
34554     
34555     
34556 });
34557
34558  
34559
34560  /**
34561  *
34562  * This is based on 
34563  * http://masonry.desandro.com
34564  *
34565  * The idea is to render all the bricks based on vertical width...
34566  *
34567  * The original code extends 'outlayer' - we might need to use that....
34568  * 
34569  */
34570
34571
34572 /**
34573  * @class Roo.bootstrap.LayoutMasonryAuto
34574  * @extends Roo.bootstrap.Component
34575  * Bootstrap Layout Masonry class
34576  * 
34577  * @constructor
34578  * Create a new Element
34579  * @param {Object} config The config object
34580  */
34581
34582 Roo.bootstrap.LayoutMasonryAuto = function(config){
34583     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34584 };
34585
34586 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34587     
34588       /**
34589      * @cfg {Boolean} isFitWidth  - resize the width..
34590      */   
34591     isFitWidth : false,  // options..
34592     /**
34593      * @cfg {Boolean} isOriginLeft = left align?
34594      */   
34595     isOriginLeft : true,
34596     /**
34597      * @cfg {Boolean} isOriginTop = top align?
34598      */   
34599     isOriginTop : false,
34600     /**
34601      * @cfg {Boolean} isLayoutInstant = no animation?
34602      */   
34603     isLayoutInstant : false, // needed?
34604     /**
34605      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34606      */   
34607     isResizingContainer : true,
34608     /**
34609      * @cfg {Number} columnWidth  width of the columns 
34610      */   
34611     
34612     columnWidth : 0,
34613     
34614     /**
34615      * @cfg {Number} maxCols maximum number of columns
34616      */   
34617     
34618     maxCols: 0,
34619     /**
34620      * @cfg {Number} padHeight padding below box..
34621      */   
34622     
34623     padHeight : 10, 
34624     
34625     /**
34626      * @cfg {Boolean} isAutoInitial defalut true
34627      */   
34628     
34629     isAutoInitial : true, 
34630     
34631     // private?
34632     gutter : 0,
34633     
34634     containerWidth: 0,
34635     initialColumnWidth : 0,
34636     currentSize : null,
34637     
34638     colYs : null, // array.
34639     maxY : 0,
34640     padWidth: 10,
34641     
34642     
34643     tag: 'div',
34644     cls: '',
34645     bricks: null, //CompositeElement
34646     cols : 0, // array?
34647     // element : null, // wrapped now this.el
34648     _isLayoutInited : null, 
34649     
34650     
34651     getAutoCreate : function(){
34652         
34653         var cfg = {
34654             tag: this.tag,
34655             cls: 'blog-masonary-wrapper ' + this.cls,
34656             cn : {
34657                 cls : 'mas-boxes masonary'
34658             }
34659         };
34660         
34661         return cfg;
34662     },
34663     
34664     getChildContainer: function( )
34665     {
34666         if (this.boxesEl) {
34667             return this.boxesEl;
34668         }
34669         
34670         this.boxesEl = this.el.select('.mas-boxes').first();
34671         
34672         return this.boxesEl;
34673     },
34674     
34675     
34676     initEvents : function()
34677     {
34678         var _this = this;
34679         
34680         if(this.isAutoInitial){
34681             Roo.log('hook children rendered');
34682             this.on('childrenrendered', function() {
34683                 Roo.log('children rendered');
34684                 _this.initial();
34685             } ,this);
34686         }
34687         
34688     },
34689     
34690     initial : function()
34691     {
34692         this.reloadItems();
34693
34694         this.currentSize = this.el.getBox(true);
34695
34696         /// was window resize... - let's see if this works..
34697         Roo.EventManager.onWindowResize(this.resize, this); 
34698
34699         if(!this.isAutoInitial){
34700             this.layout();
34701             return;
34702         }
34703         
34704         this.layout.defer(500,this);
34705     },
34706     
34707     reloadItems: function()
34708     {
34709         this.bricks = this.el.select('.masonry-brick', true);
34710         
34711         this.bricks.each(function(b) {
34712             //Roo.log(b.getSize());
34713             if (!b.attr('originalwidth')) {
34714                 b.attr('originalwidth',  b.getSize().width);
34715             }
34716             
34717         });
34718         
34719         Roo.log(this.bricks.elements.length);
34720     },
34721     
34722     resize : function()
34723     {
34724         Roo.log('resize');
34725         var cs = this.el.getBox(true);
34726         
34727         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34728             Roo.log("no change in with or X");
34729             return;
34730         }
34731         this.currentSize = cs;
34732         this.layout();
34733     },
34734     
34735     layout : function()
34736     {
34737          Roo.log('layout');
34738         this._resetLayout();
34739         //this._manageStamps();
34740       
34741         // don't animate first layout
34742         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34743         this.layoutItems( isInstant );
34744       
34745         // flag for initalized
34746         this._isLayoutInited = true;
34747     },
34748     
34749     layoutItems : function( isInstant )
34750     {
34751         //var items = this._getItemsForLayout( this.items );
34752         // original code supports filtering layout items.. we just ignore it..
34753         
34754         this._layoutItems( this.bricks , isInstant );
34755       
34756         this._postLayout();
34757     },
34758     _layoutItems : function ( items , isInstant)
34759     {
34760        //this.fireEvent( 'layout', this, items );
34761     
34762
34763         if ( !items || !items.elements.length ) {
34764           // no items, emit event with empty array
34765             return;
34766         }
34767
34768         var queue = [];
34769         items.each(function(item) {
34770             Roo.log("layout item");
34771             Roo.log(item);
34772             // get x/y object from method
34773             var position = this._getItemLayoutPosition( item );
34774             // enqueue
34775             position.item = item;
34776             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34777             queue.push( position );
34778         }, this);
34779       
34780         this._processLayoutQueue( queue );
34781     },
34782     /** Sets position of item in DOM
34783     * @param {Element} item
34784     * @param {Number} x - horizontal position
34785     * @param {Number} y - vertical position
34786     * @param {Boolean} isInstant - disables transitions
34787     */
34788     _processLayoutQueue : function( queue )
34789     {
34790         for ( var i=0, len = queue.length; i < len; i++ ) {
34791             var obj = queue[i];
34792             obj.item.position('absolute');
34793             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34794         }
34795     },
34796       
34797     
34798     /**
34799     * Any logic you want to do after each layout,
34800     * i.e. size the container
34801     */
34802     _postLayout : function()
34803     {
34804         this.resizeContainer();
34805     },
34806     
34807     resizeContainer : function()
34808     {
34809         if ( !this.isResizingContainer ) {
34810             return;
34811         }
34812         var size = this._getContainerSize();
34813         if ( size ) {
34814             this.el.setSize(size.width,size.height);
34815             this.boxesEl.setSize(size.width,size.height);
34816         }
34817     },
34818     
34819     
34820     
34821     _resetLayout : function()
34822     {
34823         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34824         this.colWidth = this.el.getWidth();
34825         //this.gutter = this.el.getWidth(); 
34826         
34827         this.measureColumns();
34828
34829         // reset column Y
34830         var i = this.cols;
34831         this.colYs = [];
34832         while (i--) {
34833             this.colYs.push( 0 );
34834         }
34835     
34836         this.maxY = 0;
34837     },
34838
34839     measureColumns : function()
34840     {
34841         this.getContainerWidth();
34842       // if columnWidth is 0, default to outerWidth of first item
34843         if ( !this.columnWidth ) {
34844             var firstItem = this.bricks.first();
34845             Roo.log(firstItem);
34846             this.columnWidth  = this.containerWidth;
34847             if (firstItem && firstItem.attr('originalwidth') ) {
34848                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34849             }
34850             // columnWidth fall back to item of first element
34851             Roo.log("set column width?");
34852                         this.initialColumnWidth = this.columnWidth  ;
34853
34854             // if first elem has no width, default to size of container
34855             
34856         }
34857         
34858         
34859         if (this.initialColumnWidth) {
34860             this.columnWidth = this.initialColumnWidth;
34861         }
34862         
34863         
34864             
34865         // column width is fixed at the top - however if container width get's smaller we should
34866         // reduce it...
34867         
34868         // this bit calcs how man columns..
34869             
34870         var columnWidth = this.columnWidth += this.gutter;
34871       
34872         // calculate columns
34873         var containerWidth = this.containerWidth + this.gutter;
34874         
34875         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34876         // fix rounding errors, typically with gutters
34877         var excess = columnWidth - containerWidth % columnWidth;
34878         
34879         
34880         // if overshoot is less than a pixel, round up, otherwise floor it
34881         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34882         cols = Math[ mathMethod ]( cols );
34883         this.cols = Math.max( cols, 1 );
34884         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34885         
34886          // padding positioning..
34887         var totalColWidth = this.cols * this.columnWidth;
34888         var padavail = this.containerWidth - totalColWidth;
34889         // so for 2 columns - we need 3 'pads'
34890         
34891         var padNeeded = (1+this.cols) * this.padWidth;
34892         
34893         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34894         
34895         this.columnWidth += padExtra
34896         //this.padWidth = Math.floor(padavail /  ( this.cols));
34897         
34898         // adjust colum width so that padding is fixed??
34899         
34900         // we have 3 columns ... total = width * 3
34901         // we have X left over... that should be used by 
34902         
34903         //if (this.expandC) {
34904             
34905         //}
34906         
34907         
34908         
34909     },
34910     
34911     getContainerWidth : function()
34912     {
34913        /* // container is parent if fit width
34914         var container = this.isFitWidth ? this.element.parentNode : this.element;
34915         // check that this.size and size are there
34916         // IE8 triggers resize on body size change, so they might not be
34917         
34918         var size = getSize( container );  //FIXME
34919         this.containerWidth = size && size.innerWidth; //FIXME
34920         */
34921          
34922         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34923         
34924     },
34925     
34926     _getItemLayoutPosition : function( item )  // what is item?
34927     {
34928         // we resize the item to our columnWidth..
34929       
34930         item.setWidth(this.columnWidth);
34931         item.autoBoxAdjust  = false;
34932         
34933         var sz = item.getSize();
34934  
34935         // how many columns does this brick span
34936         var remainder = this.containerWidth % this.columnWidth;
34937         
34938         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34939         // round if off by 1 pixel, otherwise use ceil
34940         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34941         colSpan = Math.min( colSpan, this.cols );
34942         
34943         // normally this should be '1' as we dont' currently allow multi width columns..
34944         
34945         var colGroup = this._getColGroup( colSpan );
34946         // get the minimum Y value from the columns
34947         var minimumY = Math.min.apply( Math, colGroup );
34948         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34949         
34950         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34951          
34952         // position the brick
34953         var position = {
34954             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34955             y: this.currentSize.y + minimumY + this.padHeight
34956         };
34957         
34958         Roo.log(position);
34959         // apply setHeight to necessary columns
34960         var setHeight = minimumY + sz.height + this.padHeight;
34961         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34962         
34963         var setSpan = this.cols + 1 - colGroup.length;
34964         for ( var i = 0; i < setSpan; i++ ) {
34965           this.colYs[ shortColIndex + i ] = setHeight ;
34966         }
34967       
34968         return position;
34969     },
34970     
34971     /**
34972      * @param {Number} colSpan - number of columns the element spans
34973      * @returns {Array} colGroup
34974      */
34975     _getColGroup : function( colSpan )
34976     {
34977         if ( colSpan < 2 ) {
34978           // if brick spans only one column, use all the column Ys
34979           return this.colYs;
34980         }
34981       
34982         var colGroup = [];
34983         // how many different places could this brick fit horizontally
34984         var groupCount = this.cols + 1 - colSpan;
34985         // for each group potential horizontal position
34986         for ( var i = 0; i < groupCount; i++ ) {
34987           // make an array of colY values for that one group
34988           var groupColYs = this.colYs.slice( i, i + colSpan );
34989           // and get the max value of the array
34990           colGroup[i] = Math.max.apply( Math, groupColYs );
34991         }
34992         return colGroup;
34993     },
34994     /*
34995     _manageStamp : function( stamp )
34996     {
34997         var stampSize =  stamp.getSize();
34998         var offset = stamp.getBox();
34999         // get the columns that this stamp affects
35000         var firstX = this.isOriginLeft ? offset.x : offset.right;
35001         var lastX = firstX + stampSize.width;
35002         var firstCol = Math.floor( firstX / this.columnWidth );
35003         firstCol = Math.max( 0, firstCol );
35004         
35005         var lastCol = Math.floor( lastX / this.columnWidth );
35006         // lastCol should not go over if multiple of columnWidth #425
35007         lastCol -= lastX % this.columnWidth ? 0 : 1;
35008         lastCol = Math.min( this.cols - 1, lastCol );
35009         
35010         // set colYs to bottom of the stamp
35011         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35012             stampSize.height;
35013             
35014         for ( var i = firstCol; i <= lastCol; i++ ) {
35015           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35016         }
35017     },
35018     */
35019     
35020     _getContainerSize : function()
35021     {
35022         this.maxY = Math.max.apply( Math, this.colYs );
35023         var size = {
35024             height: this.maxY
35025         };
35026       
35027         if ( this.isFitWidth ) {
35028             size.width = this._getContainerFitWidth();
35029         }
35030       
35031         return size;
35032     },
35033     
35034     _getContainerFitWidth : function()
35035     {
35036         var unusedCols = 0;
35037         // count unused columns
35038         var i = this.cols;
35039         while ( --i ) {
35040           if ( this.colYs[i] !== 0 ) {
35041             break;
35042           }
35043           unusedCols++;
35044         }
35045         // fit container to columns that have been used
35046         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35047     },
35048     
35049     needsResizeLayout : function()
35050     {
35051         var previousWidth = this.containerWidth;
35052         this.getContainerWidth();
35053         return previousWidth !== this.containerWidth;
35054     }
35055  
35056 });
35057
35058  
35059
35060  /*
35061  * - LGPL
35062  *
35063  * element
35064  * 
35065  */
35066
35067 /**
35068  * @class Roo.bootstrap.MasonryBrick
35069  * @extends Roo.bootstrap.Component
35070  * Bootstrap MasonryBrick class
35071  * 
35072  * @constructor
35073  * Create a new MasonryBrick
35074  * @param {Object} config The config object
35075  */
35076
35077 Roo.bootstrap.MasonryBrick = function(config){
35078     
35079     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35080     
35081     Roo.bootstrap.MasonryBrick.register(this);
35082     
35083     this.addEvents({
35084         // raw events
35085         /**
35086          * @event click
35087          * When a MasonryBrick is clcik
35088          * @param {Roo.bootstrap.MasonryBrick} this
35089          * @param {Roo.EventObject} e
35090          */
35091         "click" : true
35092     });
35093 };
35094
35095 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35096     
35097     /**
35098      * @cfg {String} title
35099      */   
35100     title : '',
35101     /**
35102      * @cfg {String} html
35103      */   
35104     html : '',
35105     /**
35106      * @cfg {String} bgimage
35107      */   
35108     bgimage : '',
35109     /**
35110      * @cfg {String} videourl
35111      */   
35112     videourl : '',
35113     /**
35114      * @cfg {String} cls
35115      */   
35116     cls : '',
35117     /**
35118      * @cfg {String} href
35119      */   
35120     href : '',
35121     /**
35122      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35123      */   
35124     size : 'xs',
35125     
35126     /**
35127      * @cfg {String} placetitle (center|bottom)
35128      */   
35129     placetitle : '',
35130     
35131     /**
35132      * @cfg {Boolean} isFitContainer defalut true
35133      */   
35134     isFitContainer : true, 
35135     
35136     /**
35137      * @cfg {Boolean} preventDefault defalut false
35138      */   
35139     preventDefault : false, 
35140     
35141     /**
35142      * @cfg {Boolean} inverse defalut false
35143      */   
35144     maskInverse : false, 
35145     
35146     getAutoCreate : function()
35147     {
35148         if(!this.isFitContainer){
35149             return this.getSplitAutoCreate();
35150         }
35151         
35152         var cls = 'masonry-brick masonry-brick-full';
35153         
35154         if(this.href.length){
35155             cls += ' masonry-brick-link';
35156         }
35157         
35158         if(this.bgimage.length){
35159             cls += ' masonry-brick-image';
35160         }
35161         
35162         if(this.maskInverse){
35163             cls += ' mask-inverse';
35164         }
35165         
35166         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35167             cls += ' enable-mask';
35168         }
35169         
35170         if(this.size){
35171             cls += ' masonry-' + this.size + '-brick';
35172         }
35173         
35174         if(this.placetitle.length){
35175             
35176             switch (this.placetitle) {
35177                 case 'center' :
35178                     cls += ' masonry-center-title';
35179                     break;
35180                 case 'bottom' :
35181                     cls += ' masonry-bottom-title';
35182                     break;
35183                 default:
35184                     break;
35185             }
35186             
35187         } else {
35188             if(!this.html.length && !this.bgimage.length){
35189                 cls += ' masonry-center-title';
35190             }
35191
35192             if(!this.html.length && this.bgimage.length){
35193                 cls += ' masonry-bottom-title';
35194             }
35195         }
35196         
35197         if(this.cls){
35198             cls += ' ' + this.cls;
35199         }
35200         
35201         var cfg = {
35202             tag: (this.href.length) ? 'a' : 'div',
35203             cls: cls,
35204             cn: [
35205                 {
35206                     tag: 'div',
35207                     cls: 'masonry-brick-mask'
35208                 },
35209                 {
35210                     tag: 'div',
35211                     cls: 'masonry-brick-paragraph',
35212                     cn: []
35213                 }
35214             ]
35215         };
35216         
35217         if(this.href.length){
35218             cfg.href = this.href;
35219         }
35220         
35221         var cn = cfg.cn[1].cn;
35222         
35223         if(this.title.length){
35224             cn.push({
35225                 tag: 'h4',
35226                 cls: 'masonry-brick-title',
35227                 html: this.title
35228             });
35229         }
35230         
35231         if(this.html.length){
35232             cn.push({
35233                 tag: 'p',
35234                 cls: 'masonry-brick-text',
35235                 html: this.html
35236             });
35237         }
35238         
35239         if (!this.title.length && !this.html.length) {
35240             cfg.cn[1].cls += ' hide';
35241         }
35242         
35243         if(this.bgimage.length){
35244             cfg.cn.push({
35245                 tag: 'img',
35246                 cls: 'masonry-brick-image-view',
35247                 src: this.bgimage
35248             });
35249         }
35250         
35251         if(this.videourl.length){
35252             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35253             // youtube support only?
35254             cfg.cn.push({
35255                 tag: 'iframe',
35256                 cls: 'masonry-brick-image-view',
35257                 src: vurl,
35258                 frameborder : 0,
35259                 allowfullscreen : true
35260             });
35261         }
35262         
35263         return cfg;
35264         
35265     },
35266     
35267     getSplitAutoCreate : function()
35268     {
35269         var cls = 'masonry-brick masonry-brick-split';
35270         
35271         if(this.href.length){
35272             cls += ' masonry-brick-link';
35273         }
35274         
35275         if(this.bgimage.length){
35276             cls += ' masonry-brick-image';
35277         }
35278         
35279         if(this.size){
35280             cls += ' masonry-' + this.size + '-brick';
35281         }
35282         
35283         switch (this.placetitle) {
35284             case 'center' :
35285                 cls += ' masonry-center-title';
35286                 break;
35287             case 'bottom' :
35288                 cls += ' masonry-bottom-title';
35289                 break;
35290             default:
35291                 if(!this.bgimage.length){
35292                     cls += ' masonry-center-title';
35293                 }
35294
35295                 if(this.bgimage.length){
35296                     cls += ' masonry-bottom-title';
35297                 }
35298                 break;
35299         }
35300         
35301         if(this.cls){
35302             cls += ' ' + this.cls;
35303         }
35304         
35305         var cfg = {
35306             tag: (this.href.length) ? 'a' : 'div',
35307             cls: cls,
35308             cn: [
35309                 {
35310                     tag: 'div',
35311                     cls: 'masonry-brick-split-head',
35312                     cn: [
35313                         {
35314                             tag: 'div',
35315                             cls: 'masonry-brick-paragraph',
35316                             cn: []
35317                         }
35318                     ]
35319                 },
35320                 {
35321                     tag: 'div',
35322                     cls: 'masonry-brick-split-body',
35323                     cn: []
35324                 }
35325             ]
35326         };
35327         
35328         if(this.href.length){
35329             cfg.href = this.href;
35330         }
35331         
35332         if(this.title.length){
35333             cfg.cn[0].cn[0].cn.push({
35334                 tag: 'h4',
35335                 cls: 'masonry-brick-title',
35336                 html: this.title
35337             });
35338         }
35339         
35340         if(this.html.length){
35341             cfg.cn[1].cn.push({
35342                 tag: 'p',
35343                 cls: 'masonry-brick-text',
35344                 html: this.html
35345             });
35346         }
35347
35348         if(this.bgimage.length){
35349             cfg.cn[0].cn.push({
35350                 tag: 'img',
35351                 cls: 'masonry-brick-image-view',
35352                 src: this.bgimage
35353             });
35354         }
35355         
35356         if(this.videourl.length){
35357             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35358             // youtube support only?
35359             cfg.cn[0].cn.cn.push({
35360                 tag: 'iframe',
35361                 cls: 'masonry-brick-image-view',
35362                 src: vurl,
35363                 frameborder : 0,
35364                 allowfullscreen : true
35365             });
35366         }
35367         
35368         return cfg;
35369     },
35370     
35371     initEvents: function() 
35372     {
35373         switch (this.size) {
35374             case 'xs' :
35375                 this.x = 1;
35376                 this.y = 1;
35377                 break;
35378             case 'sm' :
35379                 this.x = 2;
35380                 this.y = 2;
35381                 break;
35382             case 'md' :
35383             case 'md-left' :
35384             case 'md-right' :
35385                 this.x = 3;
35386                 this.y = 3;
35387                 break;
35388             case 'tall' :
35389                 this.x = 2;
35390                 this.y = 3;
35391                 break;
35392             case 'wide' :
35393                 this.x = 3;
35394                 this.y = 2;
35395                 break;
35396             case 'wide-thin' :
35397                 this.x = 3;
35398                 this.y = 1;
35399                 break;
35400                         
35401             default :
35402                 break;
35403         }
35404         
35405         if(Roo.isTouch){
35406             this.el.on('touchstart', this.onTouchStart, this);
35407             this.el.on('touchmove', this.onTouchMove, this);
35408             this.el.on('touchend', this.onTouchEnd, this);
35409             this.el.on('contextmenu', this.onContextMenu, this);
35410         } else {
35411             this.el.on('mouseenter'  ,this.enter, this);
35412             this.el.on('mouseleave', this.leave, this);
35413             this.el.on('click', this.onClick, this);
35414         }
35415         
35416         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35417             this.parent().bricks.push(this);   
35418         }
35419         
35420     },
35421     
35422     onClick: function(e, el)
35423     {
35424         var time = this.endTimer - this.startTimer;
35425         // Roo.log(e.preventDefault());
35426         if(Roo.isTouch){
35427             if(time > 1000){
35428                 e.preventDefault();
35429                 return;
35430             }
35431         }
35432         
35433         if(!this.preventDefault){
35434             return;
35435         }
35436         
35437         e.preventDefault();
35438         
35439         if (this.activeClass != '') {
35440             this.selectBrick();
35441         }
35442         
35443         this.fireEvent('click', this, e);
35444     },
35445     
35446     enter: function(e, el)
35447     {
35448         e.preventDefault();
35449         
35450         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35451             return;
35452         }
35453         
35454         if(this.bgimage.length && this.html.length){
35455             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35456         }
35457     },
35458     
35459     leave: function(e, el)
35460     {
35461         e.preventDefault();
35462         
35463         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35464             return;
35465         }
35466         
35467         if(this.bgimage.length && this.html.length){
35468             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35469         }
35470     },
35471     
35472     onTouchStart: function(e, el)
35473     {
35474 //        e.preventDefault();
35475         
35476         this.touchmoved = false;
35477         
35478         if(!this.isFitContainer){
35479             return;
35480         }
35481         
35482         if(!this.bgimage.length || !this.html.length){
35483             return;
35484         }
35485         
35486         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35487         
35488         this.timer = new Date().getTime();
35489         
35490     },
35491     
35492     onTouchMove: function(e, el)
35493     {
35494         this.touchmoved = true;
35495     },
35496     
35497     onContextMenu : function(e,el)
35498     {
35499         e.preventDefault();
35500         e.stopPropagation();
35501         return false;
35502     },
35503     
35504     onTouchEnd: function(e, el)
35505     {
35506 //        e.preventDefault();
35507         
35508         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35509         
35510             this.leave(e,el);
35511             
35512             return;
35513         }
35514         
35515         if(!this.bgimage.length || !this.html.length){
35516             
35517             if(this.href.length){
35518                 window.location.href = this.href;
35519             }
35520             
35521             return;
35522         }
35523         
35524         if(!this.isFitContainer){
35525             return;
35526         }
35527         
35528         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35529         
35530         window.location.href = this.href;
35531     },
35532     
35533     //selection on single brick only
35534     selectBrick : function() {
35535         
35536         if (!this.parentId) {
35537             return;
35538         }
35539         
35540         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35541         var index = m.selectedBrick.indexOf(this.id);
35542         
35543         if ( index > -1) {
35544             m.selectedBrick.splice(index,1);
35545             this.el.removeClass(this.activeClass);
35546             return;
35547         }
35548         
35549         for(var i = 0; i < m.selectedBrick.length; i++) {
35550             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35551             b.el.removeClass(b.activeClass);
35552         }
35553         
35554         m.selectedBrick = [];
35555         
35556         m.selectedBrick.push(this.id);
35557         this.el.addClass(this.activeClass);
35558         return;
35559     },
35560     
35561     isSelected : function(){
35562         return this.el.hasClass(this.activeClass);
35563         
35564     }
35565 });
35566
35567 Roo.apply(Roo.bootstrap.MasonryBrick, {
35568     
35569     //groups: {},
35570     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35571      /**
35572     * register a Masonry Brick
35573     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35574     */
35575     
35576     register : function(brick)
35577     {
35578         //this.groups[brick.id] = brick;
35579         this.groups.add(brick.id, brick);
35580     },
35581     /**
35582     * fetch a  masonry brick based on the masonry brick ID
35583     * @param {string} the masonry brick to add
35584     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35585     */
35586     
35587     get: function(brick_id) 
35588     {
35589         // if (typeof(this.groups[brick_id]) == 'undefined') {
35590         //     return false;
35591         // }
35592         // return this.groups[brick_id] ;
35593         
35594         if(this.groups.key(brick_id)) {
35595             return this.groups.key(brick_id);
35596         }
35597         
35598         return false;
35599     }
35600     
35601     
35602     
35603 });
35604
35605  /*
35606  * - LGPL
35607  *
35608  * element
35609  * 
35610  */
35611
35612 /**
35613  * @class Roo.bootstrap.Brick
35614  * @extends Roo.bootstrap.Component
35615  * Bootstrap Brick class
35616  * 
35617  * @constructor
35618  * Create a new Brick
35619  * @param {Object} config The config object
35620  */
35621
35622 Roo.bootstrap.Brick = function(config){
35623     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35624     
35625     this.addEvents({
35626         // raw events
35627         /**
35628          * @event click
35629          * When a Brick is click
35630          * @param {Roo.bootstrap.Brick} this
35631          * @param {Roo.EventObject} e
35632          */
35633         "click" : true
35634     });
35635 };
35636
35637 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35638     
35639     /**
35640      * @cfg {String} title
35641      */   
35642     title : '',
35643     /**
35644      * @cfg {String} html
35645      */   
35646     html : '',
35647     /**
35648      * @cfg {String} bgimage
35649      */   
35650     bgimage : '',
35651     /**
35652      * @cfg {String} cls
35653      */   
35654     cls : '',
35655     /**
35656      * @cfg {String} href
35657      */   
35658     href : '',
35659     /**
35660      * @cfg {String} video
35661      */   
35662     video : '',
35663     /**
35664      * @cfg {Boolean} square
35665      */   
35666     square : true,
35667     
35668     getAutoCreate : function()
35669     {
35670         var cls = 'roo-brick';
35671         
35672         if(this.href.length){
35673             cls += ' roo-brick-link';
35674         }
35675         
35676         if(this.bgimage.length){
35677             cls += ' roo-brick-image';
35678         }
35679         
35680         if(!this.html.length && !this.bgimage.length){
35681             cls += ' roo-brick-center-title';
35682         }
35683         
35684         if(!this.html.length && this.bgimage.length){
35685             cls += ' roo-brick-bottom-title';
35686         }
35687         
35688         if(this.cls){
35689             cls += ' ' + this.cls;
35690         }
35691         
35692         var cfg = {
35693             tag: (this.href.length) ? 'a' : 'div',
35694             cls: cls,
35695             cn: [
35696                 {
35697                     tag: 'div',
35698                     cls: 'roo-brick-paragraph',
35699                     cn: []
35700                 }
35701             ]
35702         };
35703         
35704         if(this.href.length){
35705             cfg.href = this.href;
35706         }
35707         
35708         var cn = cfg.cn[0].cn;
35709         
35710         if(this.title.length){
35711             cn.push({
35712                 tag: 'h4',
35713                 cls: 'roo-brick-title',
35714                 html: this.title
35715             });
35716         }
35717         
35718         if(this.html.length){
35719             cn.push({
35720                 tag: 'p',
35721                 cls: 'roo-brick-text',
35722                 html: this.html
35723             });
35724         } else {
35725             cn.cls += ' hide';
35726         }
35727         
35728         if(this.bgimage.length){
35729             cfg.cn.push({
35730                 tag: 'img',
35731                 cls: 'roo-brick-image-view',
35732                 src: this.bgimage
35733             });
35734         }
35735         
35736         return cfg;
35737     },
35738     
35739     initEvents: function() 
35740     {
35741         if(this.title.length || this.html.length){
35742             this.el.on('mouseenter'  ,this.enter, this);
35743             this.el.on('mouseleave', this.leave, this);
35744         }
35745         
35746         Roo.EventManager.onWindowResize(this.resize, this); 
35747         
35748         if(this.bgimage.length){
35749             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35750             this.imageEl.on('load', this.onImageLoad, this);
35751             return;
35752         }
35753         
35754         this.resize();
35755     },
35756     
35757     onImageLoad : function()
35758     {
35759         this.resize();
35760     },
35761     
35762     resize : function()
35763     {
35764         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35765         
35766         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35767         
35768         if(this.bgimage.length){
35769             var image = this.el.select('.roo-brick-image-view', true).first();
35770             
35771             image.setWidth(paragraph.getWidth());
35772             
35773             if(this.square){
35774                 image.setHeight(paragraph.getWidth());
35775             }
35776             
35777             this.el.setHeight(image.getHeight());
35778             paragraph.setHeight(image.getHeight());
35779             
35780         }
35781         
35782     },
35783     
35784     enter: function(e, el)
35785     {
35786         e.preventDefault();
35787         
35788         if(this.bgimage.length){
35789             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35790             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35791         }
35792     },
35793     
35794     leave: function(e, el)
35795     {
35796         e.preventDefault();
35797         
35798         if(this.bgimage.length){
35799             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35800             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35801         }
35802     }
35803     
35804 });
35805
35806  
35807
35808  /*
35809  * - LGPL
35810  *
35811  * Number field 
35812  */
35813
35814 /**
35815  * @class Roo.bootstrap.NumberField
35816  * @extends Roo.bootstrap.Input
35817  * Bootstrap NumberField class
35818  * 
35819  * 
35820  * 
35821  * 
35822  * @constructor
35823  * Create a new NumberField
35824  * @param {Object} config The config object
35825  */
35826
35827 Roo.bootstrap.NumberField = function(config){
35828     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35829 };
35830
35831 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35832     
35833     /**
35834      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35835      */
35836     allowDecimals : true,
35837     /**
35838      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35839      */
35840     decimalSeparator : ".",
35841     /**
35842      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35843      */
35844     decimalPrecision : 2,
35845     /**
35846      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35847      */
35848     allowNegative : true,
35849     
35850     /**
35851      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35852      */
35853     allowZero: true,
35854     /**
35855      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35856      */
35857     minValue : Number.NEGATIVE_INFINITY,
35858     /**
35859      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35860      */
35861     maxValue : Number.MAX_VALUE,
35862     /**
35863      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35864      */
35865     minText : "The minimum value for this field is {0}",
35866     /**
35867      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35868      */
35869     maxText : "The maximum value for this field is {0}",
35870     /**
35871      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35872      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35873      */
35874     nanText : "{0} is not a valid number",
35875     /**
35876      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35877      */
35878     thousandsDelimiter : false,
35879     /**
35880      * @cfg {String} valueAlign alignment of value
35881      */
35882     valueAlign : "left",
35883
35884     getAutoCreate : function()
35885     {
35886         var hiddenInput = {
35887             tag: 'input',
35888             type: 'hidden',
35889             id: Roo.id(),
35890             cls: 'hidden-number-input'
35891         };
35892         
35893         if (this.name) {
35894             hiddenInput.name = this.name;
35895         }
35896         
35897         this.name = '';
35898         
35899         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35900         
35901         this.name = hiddenInput.name;
35902         
35903         if(cfg.cn.length > 0) {
35904             cfg.cn.push(hiddenInput);
35905         }
35906         
35907         return cfg;
35908     },
35909
35910     // private
35911     initEvents : function()
35912     {   
35913         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35914         
35915         var allowed = "0123456789";
35916         
35917         if(this.allowDecimals){
35918             allowed += this.decimalSeparator;
35919         }
35920         
35921         if(this.allowNegative){
35922             allowed += "-";
35923         }
35924         
35925         if(this.thousandsDelimiter) {
35926             allowed += ",";
35927         }
35928         
35929         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35930         
35931         var keyPress = function(e){
35932             
35933             var k = e.getKey();
35934             
35935             var c = e.getCharCode();
35936             
35937             if(
35938                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35939                     allowed.indexOf(String.fromCharCode(c)) === -1
35940             ){
35941                 e.stopEvent();
35942                 return;
35943             }
35944             
35945             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35946                 return;
35947             }
35948             
35949             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35950                 e.stopEvent();
35951             }
35952         };
35953         
35954         this.el.on("keypress", keyPress, this);
35955     },
35956     
35957     validateValue : function(value)
35958     {
35959         
35960         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35961             return false;
35962         }
35963         
35964         var num = this.parseValue(value);
35965         
35966         if(isNaN(num)){
35967             this.markInvalid(String.format(this.nanText, value));
35968             return false;
35969         }
35970         
35971         if(num < this.minValue){
35972             this.markInvalid(String.format(this.minText, this.minValue));
35973             return false;
35974         }
35975         
35976         if(num > this.maxValue){
35977             this.markInvalid(String.format(this.maxText, this.maxValue));
35978             return false;
35979         }
35980         
35981         return true;
35982     },
35983
35984     getValue : function()
35985     {
35986         var v = this.hiddenEl().getValue();
35987         
35988         return this.fixPrecision(this.parseValue(v));
35989     },
35990
35991     parseValue : function(value)
35992     {
35993         if(this.thousandsDelimiter) {
35994             value += "";
35995             r = new RegExp(",", "g");
35996             value = value.replace(r, "");
35997         }
35998         
35999         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36000         return isNaN(value) ? '' : value;
36001     },
36002
36003     fixPrecision : function(value)
36004     {
36005         if(this.thousandsDelimiter) {
36006             value += "";
36007             r = new RegExp(",", "g");
36008             value = value.replace(r, "");
36009         }
36010         
36011         var nan = isNaN(value);
36012         
36013         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36014             return nan ? '' : value;
36015         }
36016         return parseFloat(value).toFixed(this.decimalPrecision);
36017     },
36018
36019     setValue : function(v)
36020     {
36021         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36022         
36023         this.value = v;
36024         
36025         if(this.rendered){
36026             
36027             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36028             
36029             this.inputEl().dom.value = (v == '') ? '' :
36030                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36031             
36032             if(!this.allowZero && v === '0') {
36033                 this.hiddenEl().dom.value = '';
36034                 this.inputEl().dom.value = '';
36035             }
36036             
36037             this.validate();
36038         }
36039     },
36040
36041     decimalPrecisionFcn : function(v)
36042     {
36043         return Math.floor(v);
36044     },
36045
36046     beforeBlur : function()
36047     {
36048         var v = this.parseValue(this.getRawValue());
36049         
36050         if(v || v === 0 || v === ''){
36051             this.setValue(v);
36052         }
36053     },
36054     
36055     hiddenEl : function()
36056     {
36057         return this.el.select('input.hidden-number-input',true).first();
36058     }
36059     
36060 });
36061
36062  
36063
36064 /*
36065 * Licence: LGPL
36066 */
36067
36068 /**
36069  * @class Roo.bootstrap.DocumentSlider
36070  * @extends Roo.bootstrap.Component
36071  * Bootstrap DocumentSlider class
36072  * 
36073  * @constructor
36074  * Create a new DocumentViewer
36075  * @param {Object} config The config object
36076  */
36077
36078 Roo.bootstrap.DocumentSlider = function(config){
36079     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36080     
36081     this.files = [];
36082     
36083     this.addEvents({
36084         /**
36085          * @event initial
36086          * Fire after initEvent
36087          * @param {Roo.bootstrap.DocumentSlider} this
36088          */
36089         "initial" : true,
36090         /**
36091          * @event update
36092          * Fire after update
36093          * @param {Roo.bootstrap.DocumentSlider} this
36094          */
36095         "update" : true,
36096         /**
36097          * @event click
36098          * Fire after click
36099          * @param {Roo.bootstrap.DocumentSlider} this
36100          */
36101         "click" : true
36102     });
36103 };
36104
36105 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36106     
36107     files : false,
36108     
36109     indicator : 0,
36110     
36111     getAutoCreate : function()
36112     {
36113         var cfg = {
36114             tag : 'div',
36115             cls : 'roo-document-slider',
36116             cn : [
36117                 {
36118                     tag : 'div',
36119                     cls : 'roo-document-slider-header',
36120                     cn : [
36121                         {
36122                             tag : 'div',
36123                             cls : 'roo-document-slider-header-title'
36124                         }
36125                     ]
36126                 },
36127                 {
36128                     tag : 'div',
36129                     cls : 'roo-document-slider-body',
36130                     cn : [
36131                         {
36132                             tag : 'div',
36133                             cls : 'roo-document-slider-prev',
36134                             cn : [
36135                                 {
36136                                     tag : 'i',
36137                                     cls : 'fa fa-chevron-left'
36138                                 }
36139                             ]
36140                         },
36141                         {
36142                             tag : 'div',
36143                             cls : 'roo-document-slider-thumb',
36144                             cn : [
36145                                 {
36146                                     tag : 'img',
36147                                     cls : 'roo-document-slider-image'
36148                                 }
36149                             ]
36150                         },
36151                         {
36152                             tag : 'div',
36153                             cls : 'roo-document-slider-next',
36154                             cn : [
36155                                 {
36156                                     tag : 'i',
36157                                     cls : 'fa fa-chevron-right'
36158                                 }
36159                             ]
36160                         }
36161                     ]
36162                 }
36163             ]
36164         };
36165         
36166         return cfg;
36167     },
36168     
36169     initEvents : function()
36170     {
36171         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36172         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36173         
36174         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36175         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36176         
36177         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36178         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36179         
36180         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36181         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36182         
36183         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36184         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36185         
36186         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36187         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36188         
36189         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36190         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36191         
36192         this.thumbEl.on('click', this.onClick, this);
36193         
36194         this.prevIndicator.on('click', this.prev, this);
36195         
36196         this.nextIndicator.on('click', this.next, this);
36197         
36198     },
36199     
36200     initial : function()
36201     {
36202         if(this.files.length){
36203             this.indicator = 1;
36204             this.update()
36205         }
36206         
36207         this.fireEvent('initial', this);
36208     },
36209     
36210     update : function()
36211     {
36212         this.imageEl.attr('src', this.files[this.indicator - 1]);
36213         
36214         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36215         
36216         this.prevIndicator.show();
36217         
36218         if(this.indicator == 1){
36219             this.prevIndicator.hide();
36220         }
36221         
36222         this.nextIndicator.show();
36223         
36224         if(this.indicator == this.files.length){
36225             this.nextIndicator.hide();
36226         }
36227         
36228         this.thumbEl.scrollTo('top');
36229         
36230         this.fireEvent('update', this);
36231     },
36232     
36233     onClick : function(e)
36234     {
36235         e.preventDefault();
36236         
36237         this.fireEvent('click', this);
36238     },
36239     
36240     prev : function(e)
36241     {
36242         e.preventDefault();
36243         
36244         this.indicator = Math.max(1, this.indicator - 1);
36245         
36246         this.update();
36247     },
36248     
36249     next : function(e)
36250     {
36251         e.preventDefault();
36252         
36253         this.indicator = Math.min(this.files.length, this.indicator + 1);
36254         
36255         this.update();
36256     }
36257 });
36258 /*
36259  * - LGPL
36260  *
36261  * RadioSet
36262  *
36263  *
36264  */
36265
36266 /**
36267  * @class Roo.bootstrap.RadioSet
36268  * @extends Roo.bootstrap.Input
36269  * Bootstrap RadioSet class
36270  * @cfg {String} indicatorpos (left|right) default left
36271  * @cfg {Boolean} inline (true|false) inline the element (default true)
36272  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36273  * @constructor
36274  * Create a new RadioSet
36275  * @param {Object} config The config object
36276  */
36277
36278 Roo.bootstrap.RadioSet = function(config){
36279     
36280     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36281     
36282     this.radioes = [];
36283     
36284     Roo.bootstrap.RadioSet.register(this);
36285     
36286     this.addEvents({
36287         /**
36288         * @event check
36289         * Fires when the element is checked or unchecked.
36290         * @param {Roo.bootstrap.RadioSet} this This radio
36291         * @param {Roo.bootstrap.Radio} item The checked item
36292         */
36293        check : true,
36294        /**
36295         * @event click
36296         * Fires when the element is click.
36297         * @param {Roo.bootstrap.RadioSet} this This radio set
36298         * @param {Roo.bootstrap.Radio} item The checked item
36299         * @param {Roo.EventObject} e The event object
36300         */
36301        click : true
36302     });
36303     
36304 };
36305
36306 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36307
36308     radioes : false,
36309     
36310     inline : true,
36311     
36312     weight : '',
36313     
36314     indicatorpos : 'left',
36315     
36316     getAutoCreate : function()
36317     {
36318         var label = {
36319             tag : 'label',
36320             cls : 'roo-radio-set-label',
36321             cn : [
36322                 {
36323                     tag : 'span',
36324                     html : this.fieldLabel
36325                 }
36326             ]
36327         };
36328         if (Roo.bootstrap.version == 3) {
36329             
36330             
36331             if(this.indicatorpos == 'left'){
36332                 label.cn.unshift({
36333                     tag : 'i',
36334                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36335                     tooltip : 'This field is required'
36336                 });
36337             } else {
36338                 label.cn.push({
36339                     tag : 'i',
36340                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36341                     tooltip : 'This field is required'
36342                 });
36343             }
36344         }
36345         var items = {
36346             tag : 'div',
36347             cls : 'roo-radio-set-items'
36348         };
36349         
36350         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36351         
36352         if (align === 'left' && this.fieldLabel.length) {
36353             
36354             items = {
36355                 cls : "roo-radio-set-right", 
36356                 cn: [
36357                     items
36358                 ]
36359             };
36360             
36361             if(this.labelWidth > 12){
36362                 label.style = "width: " + this.labelWidth + 'px';
36363             }
36364             
36365             if(this.labelWidth < 13 && this.labelmd == 0){
36366                 this.labelmd = this.labelWidth;
36367             }
36368             
36369             if(this.labellg > 0){
36370                 label.cls += ' col-lg-' + this.labellg;
36371                 items.cls += ' col-lg-' + (12 - this.labellg);
36372             }
36373             
36374             if(this.labelmd > 0){
36375                 label.cls += ' col-md-' + this.labelmd;
36376                 items.cls += ' col-md-' + (12 - this.labelmd);
36377             }
36378             
36379             if(this.labelsm > 0){
36380                 label.cls += ' col-sm-' + this.labelsm;
36381                 items.cls += ' col-sm-' + (12 - this.labelsm);
36382             }
36383             
36384             if(this.labelxs > 0){
36385                 label.cls += ' col-xs-' + this.labelxs;
36386                 items.cls += ' col-xs-' + (12 - this.labelxs);
36387             }
36388         }
36389         
36390         var cfg = {
36391             tag : 'div',
36392             cls : 'roo-radio-set',
36393             cn : [
36394                 {
36395                     tag : 'input',
36396                     cls : 'roo-radio-set-input',
36397                     type : 'hidden',
36398                     name : this.name,
36399                     value : this.value ? this.value :  ''
36400                 },
36401                 label,
36402                 items
36403             ]
36404         };
36405         
36406         if(this.weight.length){
36407             cfg.cls += ' roo-radio-' + this.weight;
36408         }
36409         
36410         if(this.inline) {
36411             cfg.cls += ' roo-radio-set-inline';
36412         }
36413         
36414         var settings=this;
36415         ['xs','sm','md','lg'].map(function(size){
36416             if (settings[size]) {
36417                 cfg.cls += ' col-' + size + '-' + settings[size];
36418             }
36419         });
36420         
36421         return cfg;
36422         
36423     },
36424
36425     initEvents : function()
36426     {
36427         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36428         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36429         
36430         if(!this.fieldLabel.length){
36431             this.labelEl.hide();
36432         }
36433         
36434         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36435         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36436         
36437         this.indicator = this.indicatorEl();
36438         
36439         if(this.indicator){
36440             this.indicator.addClass('invisible');
36441         }
36442         
36443         this.originalValue = this.getValue();
36444         
36445     },
36446     
36447     inputEl: function ()
36448     {
36449         return this.el.select('.roo-radio-set-input', true).first();
36450     },
36451     
36452     getChildContainer : function()
36453     {
36454         return this.itemsEl;
36455     },
36456     
36457     register : function(item)
36458     {
36459         this.radioes.push(item);
36460         
36461     },
36462     
36463     validate : function()
36464     {   
36465         if(this.getVisibilityEl().hasClass('hidden')){
36466             return true;
36467         }
36468         
36469         var valid = false;
36470         
36471         Roo.each(this.radioes, function(i){
36472             if(!i.checked){
36473                 return;
36474             }
36475             
36476             valid = true;
36477             return false;
36478         });
36479         
36480         if(this.allowBlank) {
36481             return true;
36482         }
36483         
36484         if(this.disabled || valid){
36485             this.markValid();
36486             return true;
36487         }
36488         
36489         this.markInvalid();
36490         return false;
36491         
36492     },
36493     
36494     markValid : function()
36495     {
36496         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36497             this.indicatorEl().removeClass('visible');
36498             this.indicatorEl().addClass('invisible');
36499         }
36500         
36501         
36502         if (Roo.bootstrap.version == 3) {
36503             this.el.removeClass([this.invalidClass, this.validClass]);
36504             this.el.addClass(this.validClass);
36505         } else {
36506             this.el.removeClass(['is-invalid','is-valid']);
36507             this.el.addClass(['is-valid']);
36508         }
36509         this.fireEvent('valid', this);
36510     },
36511     
36512     markInvalid : function(msg)
36513     {
36514         if(this.allowBlank || this.disabled){
36515             return;
36516         }
36517         
36518         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36519             this.indicatorEl().removeClass('invisible');
36520             this.indicatorEl().addClass('visible');
36521         }
36522         if (Roo.bootstrap.version == 3) {
36523             this.el.removeClass([this.invalidClass, this.validClass]);
36524             this.el.addClass(this.invalidClass);
36525         } else {
36526             this.el.removeClass(['is-invalid','is-valid']);
36527             this.el.addClass(['is-invalid']);
36528         }
36529         
36530         this.fireEvent('invalid', this, msg);
36531         
36532     },
36533     
36534     setValue : function(v, suppressEvent)
36535     {   
36536         if(this.value === v){
36537             return;
36538         }
36539         
36540         this.value = v;
36541         
36542         if(this.rendered){
36543             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36544         }
36545         
36546         Roo.each(this.radioes, function(i){
36547             i.checked = false;
36548             i.el.removeClass('checked');
36549         });
36550         
36551         Roo.each(this.radioes, function(i){
36552             
36553             if(i.value === v || i.value.toString() === v.toString()){
36554                 i.checked = true;
36555                 i.el.addClass('checked');
36556                 
36557                 if(suppressEvent !== true){
36558                     this.fireEvent('check', this, i);
36559                 }
36560                 
36561                 return false;
36562             }
36563             
36564         }, this);
36565         
36566         this.validate();
36567     },
36568     
36569     clearInvalid : function(){
36570         
36571         if(!this.el || this.preventMark){
36572             return;
36573         }
36574         
36575         this.el.removeClass([this.invalidClass]);
36576         
36577         this.fireEvent('valid', this);
36578     }
36579     
36580 });
36581
36582 Roo.apply(Roo.bootstrap.RadioSet, {
36583     
36584     groups: {},
36585     
36586     register : function(set)
36587     {
36588         this.groups[set.name] = set;
36589     },
36590     
36591     get: function(name) 
36592     {
36593         if (typeof(this.groups[name]) == 'undefined') {
36594             return false;
36595         }
36596         
36597         return this.groups[name] ;
36598     }
36599     
36600 });
36601 /*
36602  * Based on:
36603  * Ext JS Library 1.1.1
36604  * Copyright(c) 2006-2007, Ext JS, LLC.
36605  *
36606  * Originally Released Under LGPL - original licence link has changed is not relivant.
36607  *
36608  * Fork - LGPL
36609  * <script type="text/javascript">
36610  */
36611
36612
36613 /**
36614  * @class Roo.bootstrap.SplitBar
36615  * @extends Roo.util.Observable
36616  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36617  * <br><br>
36618  * Usage:
36619  * <pre><code>
36620 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36621                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36622 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36623 split.minSize = 100;
36624 split.maxSize = 600;
36625 split.animate = true;
36626 split.on('moved', splitterMoved);
36627 </code></pre>
36628  * @constructor
36629  * Create a new SplitBar
36630  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36631  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36632  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36633  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36634                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36635                         position of the SplitBar).
36636  */
36637 Roo.bootstrap.SplitBar = function(cfg){
36638     
36639     /** @private */
36640     
36641     //{
36642     //  dragElement : elm
36643     //  resizingElement: el,
36644         // optional..
36645     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36646     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36647         // existingProxy ???
36648     //}
36649     
36650     this.el = Roo.get(cfg.dragElement, true);
36651     this.el.dom.unselectable = "on";
36652     /** @private */
36653     this.resizingEl = Roo.get(cfg.resizingElement, true);
36654
36655     /**
36656      * @private
36657      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36658      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36659      * @type Number
36660      */
36661     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36662     
36663     /**
36664      * The minimum size of the resizing element. (Defaults to 0)
36665      * @type Number
36666      */
36667     this.minSize = 0;
36668     
36669     /**
36670      * The maximum size of the resizing element. (Defaults to 2000)
36671      * @type Number
36672      */
36673     this.maxSize = 2000;
36674     
36675     /**
36676      * Whether to animate the transition to the new size
36677      * @type Boolean
36678      */
36679     this.animate = false;
36680     
36681     /**
36682      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36683      * @type Boolean
36684      */
36685     this.useShim = false;
36686     
36687     /** @private */
36688     this.shim = null;
36689     
36690     if(!cfg.existingProxy){
36691         /** @private */
36692         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36693     }else{
36694         this.proxy = Roo.get(cfg.existingProxy).dom;
36695     }
36696     /** @private */
36697     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36698     
36699     /** @private */
36700     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36701     
36702     /** @private */
36703     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36704     
36705     /** @private */
36706     this.dragSpecs = {};
36707     
36708     /**
36709      * @private The adapter to use to positon and resize elements
36710      */
36711     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36712     this.adapter.init(this);
36713     
36714     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36715         /** @private */
36716         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36717         this.el.addClass("roo-splitbar-h");
36718     }else{
36719         /** @private */
36720         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36721         this.el.addClass("roo-splitbar-v");
36722     }
36723     
36724     this.addEvents({
36725         /**
36726          * @event resize
36727          * Fires when the splitter is moved (alias for {@link #event-moved})
36728          * @param {Roo.bootstrap.SplitBar} this
36729          * @param {Number} newSize the new width or height
36730          */
36731         "resize" : true,
36732         /**
36733          * @event moved
36734          * Fires when the splitter is moved
36735          * @param {Roo.bootstrap.SplitBar} this
36736          * @param {Number} newSize the new width or height
36737          */
36738         "moved" : true,
36739         /**
36740          * @event beforeresize
36741          * Fires before the splitter is dragged
36742          * @param {Roo.bootstrap.SplitBar} this
36743          */
36744         "beforeresize" : true,
36745
36746         "beforeapply" : true
36747     });
36748
36749     Roo.util.Observable.call(this);
36750 };
36751
36752 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36753     onStartProxyDrag : function(x, y){
36754         this.fireEvent("beforeresize", this);
36755         if(!this.overlay){
36756             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36757             o.unselectable();
36758             o.enableDisplayMode("block");
36759             // all splitbars share the same overlay
36760             Roo.bootstrap.SplitBar.prototype.overlay = o;
36761         }
36762         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36763         this.overlay.show();
36764         Roo.get(this.proxy).setDisplayed("block");
36765         var size = this.adapter.getElementSize(this);
36766         this.activeMinSize = this.getMinimumSize();;
36767         this.activeMaxSize = this.getMaximumSize();;
36768         var c1 = size - this.activeMinSize;
36769         var c2 = Math.max(this.activeMaxSize - size, 0);
36770         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36771             this.dd.resetConstraints();
36772             this.dd.setXConstraint(
36773                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36774                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36775             );
36776             this.dd.setYConstraint(0, 0);
36777         }else{
36778             this.dd.resetConstraints();
36779             this.dd.setXConstraint(0, 0);
36780             this.dd.setYConstraint(
36781                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36782                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36783             );
36784          }
36785         this.dragSpecs.startSize = size;
36786         this.dragSpecs.startPoint = [x, y];
36787         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36788     },
36789     
36790     /** 
36791      * @private Called after the drag operation by the DDProxy
36792      */
36793     onEndProxyDrag : function(e){
36794         Roo.get(this.proxy).setDisplayed(false);
36795         var endPoint = Roo.lib.Event.getXY(e);
36796         if(this.overlay){
36797             this.overlay.hide();
36798         }
36799         var newSize;
36800         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36801             newSize = this.dragSpecs.startSize + 
36802                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36803                     endPoint[0] - this.dragSpecs.startPoint[0] :
36804                     this.dragSpecs.startPoint[0] - endPoint[0]
36805                 );
36806         }else{
36807             newSize = this.dragSpecs.startSize + 
36808                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36809                     endPoint[1] - this.dragSpecs.startPoint[1] :
36810                     this.dragSpecs.startPoint[1] - endPoint[1]
36811                 );
36812         }
36813         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36814         if(newSize != this.dragSpecs.startSize){
36815             if(this.fireEvent('beforeapply', this, newSize) !== false){
36816                 this.adapter.setElementSize(this, newSize);
36817                 this.fireEvent("moved", this, newSize);
36818                 this.fireEvent("resize", this, newSize);
36819             }
36820         }
36821     },
36822     
36823     /**
36824      * Get the adapter this SplitBar uses
36825      * @return The adapter object
36826      */
36827     getAdapter : function(){
36828         return this.adapter;
36829     },
36830     
36831     /**
36832      * Set the adapter this SplitBar uses
36833      * @param {Object} adapter A SplitBar adapter object
36834      */
36835     setAdapter : function(adapter){
36836         this.adapter = adapter;
36837         this.adapter.init(this);
36838     },
36839     
36840     /**
36841      * Gets the minimum size for the resizing element
36842      * @return {Number} The minimum size
36843      */
36844     getMinimumSize : function(){
36845         return this.minSize;
36846     },
36847     
36848     /**
36849      * Sets the minimum size for the resizing element
36850      * @param {Number} minSize The minimum size
36851      */
36852     setMinimumSize : function(minSize){
36853         this.minSize = minSize;
36854     },
36855     
36856     /**
36857      * Gets the maximum size for the resizing element
36858      * @return {Number} The maximum size
36859      */
36860     getMaximumSize : function(){
36861         return this.maxSize;
36862     },
36863     
36864     /**
36865      * Sets the maximum size for the resizing element
36866      * @param {Number} maxSize The maximum size
36867      */
36868     setMaximumSize : function(maxSize){
36869         this.maxSize = maxSize;
36870     },
36871     
36872     /**
36873      * Sets the initialize size for the resizing element
36874      * @param {Number} size The initial size
36875      */
36876     setCurrentSize : function(size){
36877         var oldAnimate = this.animate;
36878         this.animate = false;
36879         this.adapter.setElementSize(this, size);
36880         this.animate = oldAnimate;
36881     },
36882     
36883     /**
36884      * Destroy this splitbar. 
36885      * @param {Boolean} removeEl True to remove the element
36886      */
36887     destroy : function(removeEl){
36888         if(this.shim){
36889             this.shim.remove();
36890         }
36891         this.dd.unreg();
36892         this.proxy.parentNode.removeChild(this.proxy);
36893         if(removeEl){
36894             this.el.remove();
36895         }
36896     }
36897 });
36898
36899 /**
36900  * @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.
36901  */
36902 Roo.bootstrap.SplitBar.createProxy = function(dir){
36903     var proxy = new Roo.Element(document.createElement("div"));
36904     proxy.unselectable();
36905     var cls = 'roo-splitbar-proxy';
36906     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36907     document.body.appendChild(proxy.dom);
36908     return proxy.dom;
36909 };
36910
36911 /** 
36912  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36913  * Default Adapter. It assumes the splitter and resizing element are not positioned
36914  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36915  */
36916 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36917 };
36918
36919 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36920     // do nothing for now
36921     init : function(s){
36922     
36923     },
36924     /**
36925      * Called before drag operations to get the current size of the resizing element. 
36926      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36927      */
36928      getElementSize : function(s){
36929         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36930             return s.resizingEl.getWidth();
36931         }else{
36932             return s.resizingEl.getHeight();
36933         }
36934     },
36935     
36936     /**
36937      * Called after drag operations to set the size of the resizing element.
36938      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36939      * @param {Number} newSize The new size to set
36940      * @param {Function} onComplete A function to be invoked when resizing is complete
36941      */
36942     setElementSize : function(s, newSize, onComplete){
36943         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36944             if(!s.animate){
36945                 s.resizingEl.setWidth(newSize);
36946                 if(onComplete){
36947                     onComplete(s, newSize);
36948                 }
36949             }else{
36950                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36951             }
36952         }else{
36953             
36954             if(!s.animate){
36955                 s.resizingEl.setHeight(newSize);
36956                 if(onComplete){
36957                     onComplete(s, newSize);
36958                 }
36959             }else{
36960                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36961             }
36962         }
36963     }
36964 };
36965
36966 /** 
36967  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36968  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36969  * Adapter that  moves the splitter element to align with the resized sizing element. 
36970  * Used with an absolute positioned SplitBar.
36971  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36972  * document.body, make sure you assign an id to the body element.
36973  */
36974 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36975     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36976     this.container = Roo.get(container);
36977 };
36978
36979 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36980     init : function(s){
36981         this.basic.init(s);
36982     },
36983     
36984     getElementSize : function(s){
36985         return this.basic.getElementSize(s);
36986     },
36987     
36988     setElementSize : function(s, newSize, onComplete){
36989         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36990     },
36991     
36992     moveSplitter : function(s){
36993         var yes = Roo.bootstrap.SplitBar;
36994         switch(s.placement){
36995             case yes.LEFT:
36996                 s.el.setX(s.resizingEl.getRight());
36997                 break;
36998             case yes.RIGHT:
36999                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37000                 break;
37001             case yes.TOP:
37002                 s.el.setY(s.resizingEl.getBottom());
37003                 break;
37004             case yes.BOTTOM:
37005                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37006                 break;
37007         }
37008     }
37009 };
37010
37011 /**
37012  * Orientation constant - Create a vertical SplitBar
37013  * @static
37014  * @type Number
37015  */
37016 Roo.bootstrap.SplitBar.VERTICAL = 1;
37017
37018 /**
37019  * Orientation constant - Create a horizontal SplitBar
37020  * @static
37021  * @type Number
37022  */
37023 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37024
37025 /**
37026  * Placement constant - The resizing element is to the left of the splitter element
37027  * @static
37028  * @type Number
37029  */
37030 Roo.bootstrap.SplitBar.LEFT = 1;
37031
37032 /**
37033  * Placement constant - The resizing element is to the right of the splitter element
37034  * @static
37035  * @type Number
37036  */
37037 Roo.bootstrap.SplitBar.RIGHT = 2;
37038
37039 /**
37040  * Placement constant - The resizing element is positioned above the splitter element
37041  * @static
37042  * @type Number
37043  */
37044 Roo.bootstrap.SplitBar.TOP = 3;
37045
37046 /**
37047  * Placement constant - The resizing element is positioned under splitter element
37048  * @static
37049  * @type Number
37050  */
37051 Roo.bootstrap.SplitBar.BOTTOM = 4;
37052 Roo.namespace("Roo.bootstrap.layout");/*
37053  * Based on:
37054  * Ext JS Library 1.1.1
37055  * Copyright(c) 2006-2007, Ext JS, LLC.
37056  *
37057  * Originally Released Under LGPL - original licence link has changed is not relivant.
37058  *
37059  * Fork - LGPL
37060  * <script type="text/javascript">
37061  */
37062
37063 /**
37064  * @class Roo.bootstrap.layout.Manager
37065  * @extends Roo.bootstrap.Component
37066  * Base class for layout managers.
37067  */
37068 Roo.bootstrap.layout.Manager = function(config)
37069 {
37070     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37071
37072
37073
37074
37075
37076     /** false to disable window resize monitoring @type Boolean */
37077     this.monitorWindowResize = true;
37078     this.regions = {};
37079     this.addEvents({
37080         /**
37081          * @event layout
37082          * Fires when a layout is performed.
37083          * @param {Roo.LayoutManager} this
37084          */
37085         "layout" : true,
37086         /**
37087          * @event regionresized
37088          * Fires when the user resizes a region.
37089          * @param {Roo.LayoutRegion} region The resized region
37090          * @param {Number} newSize The new size (width for east/west, height for north/south)
37091          */
37092         "regionresized" : true,
37093         /**
37094          * @event regioncollapsed
37095          * Fires when a region is collapsed.
37096          * @param {Roo.LayoutRegion} region The collapsed region
37097          */
37098         "regioncollapsed" : true,
37099         /**
37100          * @event regionexpanded
37101          * Fires when a region is expanded.
37102          * @param {Roo.LayoutRegion} region The expanded region
37103          */
37104         "regionexpanded" : true
37105     });
37106     this.updating = false;
37107
37108     if (config.el) {
37109         this.el = Roo.get(config.el);
37110         this.initEvents();
37111     }
37112
37113 };
37114
37115 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37116
37117
37118     regions : null,
37119
37120     monitorWindowResize : true,
37121
37122
37123     updating : false,
37124
37125
37126     onRender : function(ct, position)
37127     {
37128         if(!this.el){
37129             this.el = Roo.get(ct);
37130             this.initEvents();
37131         }
37132         //this.fireEvent('render',this);
37133     },
37134
37135
37136     initEvents: function()
37137     {
37138
37139
37140         // ie scrollbar fix
37141         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37142             document.body.scroll = "no";
37143         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37144             this.el.position('relative');
37145         }
37146         this.id = this.el.id;
37147         this.el.addClass("roo-layout-container");
37148         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37149         if(this.el.dom != document.body ) {
37150             this.el.on('resize', this.layout,this);
37151             this.el.on('show', this.layout,this);
37152         }
37153
37154     },
37155
37156     /**
37157      * Returns true if this layout is currently being updated
37158      * @return {Boolean}
37159      */
37160     isUpdating : function(){
37161         return this.updating;
37162     },
37163
37164     /**
37165      * Suspend the LayoutManager from doing auto-layouts while
37166      * making multiple add or remove calls
37167      */
37168     beginUpdate : function(){
37169         this.updating = true;
37170     },
37171
37172     /**
37173      * Restore auto-layouts and optionally disable the manager from performing a layout
37174      * @param {Boolean} noLayout true to disable a layout update
37175      */
37176     endUpdate : function(noLayout){
37177         this.updating = false;
37178         if(!noLayout){
37179             this.layout();
37180         }
37181     },
37182
37183     layout: function(){
37184         // abstract...
37185     },
37186
37187     onRegionResized : function(region, newSize){
37188         this.fireEvent("regionresized", region, newSize);
37189         this.layout();
37190     },
37191
37192     onRegionCollapsed : function(region){
37193         this.fireEvent("regioncollapsed", region);
37194     },
37195
37196     onRegionExpanded : function(region){
37197         this.fireEvent("regionexpanded", region);
37198     },
37199
37200     /**
37201      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37202      * performs box-model adjustments.
37203      * @return {Object} The size as an object {width: (the width), height: (the height)}
37204      */
37205     getViewSize : function()
37206     {
37207         var size;
37208         if(this.el.dom != document.body){
37209             size = this.el.getSize();
37210         }else{
37211             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37212         }
37213         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37214         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37215         return size;
37216     },
37217
37218     /**
37219      * Returns the Element this layout is bound to.
37220      * @return {Roo.Element}
37221      */
37222     getEl : function(){
37223         return this.el;
37224     },
37225
37226     /**
37227      * Returns the specified region.
37228      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37229      * @return {Roo.LayoutRegion}
37230      */
37231     getRegion : function(target){
37232         return this.regions[target.toLowerCase()];
37233     },
37234
37235     onWindowResize : function(){
37236         if(this.monitorWindowResize){
37237             this.layout();
37238         }
37239     }
37240 });
37241 /*
37242  * Based on:
37243  * Ext JS Library 1.1.1
37244  * Copyright(c) 2006-2007, Ext JS, LLC.
37245  *
37246  * Originally Released Under LGPL - original licence link has changed is not relivant.
37247  *
37248  * Fork - LGPL
37249  * <script type="text/javascript">
37250  */
37251 /**
37252  * @class Roo.bootstrap.layout.Border
37253  * @extends Roo.bootstrap.layout.Manager
37254  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37255  * please see: examples/bootstrap/nested.html<br><br>
37256  
37257 <b>The container the layout is rendered into can be either the body element or any other element.
37258 If it is not the body element, the container needs to either be an absolute positioned element,
37259 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37260 the container size if it is not the body element.</b>
37261
37262 * @constructor
37263 * Create a new Border
37264 * @param {Object} config Configuration options
37265  */
37266 Roo.bootstrap.layout.Border = function(config){
37267     config = config || {};
37268     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37269     
37270     
37271     
37272     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37273         if(config[region]){
37274             config[region].region = region;
37275             this.addRegion(config[region]);
37276         }
37277     },this);
37278     
37279 };
37280
37281 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37282
37283 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37284     
37285     parent : false, // this might point to a 'nest' or a ???
37286     
37287     /**
37288      * Creates and adds a new region if it doesn't already exist.
37289      * @param {String} target The target region key (north, south, east, west or center).
37290      * @param {Object} config The regions config object
37291      * @return {BorderLayoutRegion} The new region
37292      */
37293     addRegion : function(config)
37294     {
37295         if(!this.regions[config.region]){
37296             var r = this.factory(config);
37297             this.bindRegion(r);
37298         }
37299         return this.regions[config.region];
37300     },
37301
37302     // private (kinda)
37303     bindRegion : function(r){
37304         this.regions[r.config.region] = r;
37305         
37306         r.on("visibilitychange",    this.layout, this);
37307         r.on("paneladded",          this.layout, this);
37308         r.on("panelremoved",        this.layout, this);
37309         r.on("invalidated",         this.layout, this);
37310         r.on("resized",             this.onRegionResized, this);
37311         r.on("collapsed",           this.onRegionCollapsed, this);
37312         r.on("expanded",            this.onRegionExpanded, this);
37313     },
37314
37315     /**
37316      * Performs a layout update.
37317      */
37318     layout : function()
37319     {
37320         if(this.updating) {
37321             return;
37322         }
37323         
37324         // render all the rebions if they have not been done alreayd?
37325         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37326             if(this.regions[region] && !this.regions[region].bodyEl){
37327                 this.regions[region].onRender(this.el)
37328             }
37329         },this);
37330         
37331         var size = this.getViewSize();
37332         var w = size.width;
37333         var h = size.height;
37334         var centerW = w;
37335         var centerH = h;
37336         var centerY = 0;
37337         var centerX = 0;
37338         //var x = 0, y = 0;
37339
37340         var rs = this.regions;
37341         var north = rs["north"];
37342         var south = rs["south"]; 
37343         var west = rs["west"];
37344         var east = rs["east"];
37345         var center = rs["center"];
37346         //if(this.hideOnLayout){ // not supported anymore
37347             //c.el.setStyle("display", "none");
37348         //}
37349         if(north && north.isVisible()){
37350             var b = north.getBox();
37351             var m = north.getMargins();
37352             b.width = w - (m.left+m.right);
37353             b.x = m.left;
37354             b.y = m.top;
37355             centerY = b.height + b.y + m.bottom;
37356             centerH -= centerY;
37357             north.updateBox(this.safeBox(b));
37358         }
37359         if(south && south.isVisible()){
37360             var b = south.getBox();
37361             var m = south.getMargins();
37362             b.width = w - (m.left+m.right);
37363             b.x = m.left;
37364             var totalHeight = (b.height + m.top + m.bottom);
37365             b.y = h - totalHeight + m.top;
37366             centerH -= totalHeight;
37367             south.updateBox(this.safeBox(b));
37368         }
37369         if(west && west.isVisible()){
37370             var b = west.getBox();
37371             var m = west.getMargins();
37372             b.height = centerH - (m.top+m.bottom);
37373             b.x = m.left;
37374             b.y = centerY + m.top;
37375             var totalWidth = (b.width + m.left + m.right);
37376             centerX += totalWidth;
37377             centerW -= totalWidth;
37378             west.updateBox(this.safeBox(b));
37379         }
37380         if(east && east.isVisible()){
37381             var b = east.getBox();
37382             var m = east.getMargins();
37383             b.height = centerH - (m.top+m.bottom);
37384             var totalWidth = (b.width + m.left + m.right);
37385             b.x = w - totalWidth + m.left;
37386             b.y = centerY + m.top;
37387             centerW -= totalWidth;
37388             east.updateBox(this.safeBox(b));
37389         }
37390         if(center){
37391             var m = center.getMargins();
37392             var centerBox = {
37393                 x: centerX + m.left,
37394                 y: centerY + m.top,
37395                 width: centerW - (m.left+m.right),
37396                 height: centerH - (m.top+m.bottom)
37397             };
37398             //if(this.hideOnLayout){
37399                 //center.el.setStyle("display", "block");
37400             //}
37401             center.updateBox(this.safeBox(centerBox));
37402         }
37403         this.el.repaint();
37404         this.fireEvent("layout", this);
37405     },
37406
37407     // private
37408     safeBox : function(box){
37409         box.width = Math.max(0, box.width);
37410         box.height = Math.max(0, box.height);
37411         return box;
37412     },
37413
37414     /**
37415      * Adds a ContentPanel (or subclass) to this layout.
37416      * @param {String} target The target region key (north, south, east, west or center).
37417      * @param {Roo.ContentPanel} panel The panel to add
37418      * @return {Roo.ContentPanel} The added panel
37419      */
37420     add : function(target, panel){
37421          
37422         target = target.toLowerCase();
37423         return this.regions[target].add(panel);
37424     },
37425
37426     /**
37427      * Remove a ContentPanel (or subclass) to this layout.
37428      * @param {String} target The target region key (north, south, east, west or center).
37429      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37430      * @return {Roo.ContentPanel} The removed panel
37431      */
37432     remove : function(target, panel){
37433         target = target.toLowerCase();
37434         return this.regions[target].remove(panel);
37435     },
37436
37437     /**
37438      * Searches all regions for a panel with the specified id
37439      * @param {String} panelId
37440      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37441      */
37442     findPanel : function(panelId){
37443         var rs = this.regions;
37444         for(var target in rs){
37445             if(typeof rs[target] != "function"){
37446                 var p = rs[target].getPanel(panelId);
37447                 if(p){
37448                     return p;
37449                 }
37450             }
37451         }
37452         return null;
37453     },
37454
37455     /**
37456      * Searches all regions for a panel with the specified id and activates (shows) it.
37457      * @param {String/ContentPanel} panelId The panels id or the panel itself
37458      * @return {Roo.ContentPanel} The shown panel or null
37459      */
37460     showPanel : function(panelId) {
37461       var rs = this.regions;
37462       for(var target in rs){
37463          var r = rs[target];
37464          if(typeof r != "function"){
37465             if(r.hasPanel(panelId)){
37466                return r.showPanel(panelId);
37467             }
37468          }
37469       }
37470       return null;
37471    },
37472
37473    /**
37474      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37475      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37476      */
37477    /*
37478     restoreState : function(provider){
37479         if(!provider){
37480             provider = Roo.state.Manager;
37481         }
37482         var sm = new Roo.LayoutStateManager();
37483         sm.init(this, provider);
37484     },
37485 */
37486  
37487  
37488     /**
37489      * Adds a xtype elements to the layout.
37490      * <pre><code>
37491
37492 layout.addxtype({
37493        xtype : 'ContentPanel',
37494        region: 'west',
37495        items: [ .... ]
37496    }
37497 );
37498
37499 layout.addxtype({
37500         xtype : 'NestedLayoutPanel',
37501         region: 'west',
37502         layout: {
37503            center: { },
37504            west: { }   
37505         },
37506         items : [ ... list of content panels or nested layout panels.. ]
37507    }
37508 );
37509 </code></pre>
37510      * @param {Object} cfg Xtype definition of item to add.
37511      */
37512     addxtype : function(cfg)
37513     {
37514         // basically accepts a pannel...
37515         // can accept a layout region..!?!?
37516         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37517         
37518         
37519         // theory?  children can only be panels??
37520         
37521         //if (!cfg.xtype.match(/Panel$/)) {
37522         //    return false;
37523         //}
37524         var ret = false;
37525         
37526         if (typeof(cfg.region) == 'undefined') {
37527             Roo.log("Failed to add Panel, region was not set");
37528             Roo.log(cfg);
37529             return false;
37530         }
37531         var region = cfg.region;
37532         delete cfg.region;
37533         
37534           
37535         var xitems = [];
37536         if (cfg.items) {
37537             xitems = cfg.items;
37538             delete cfg.items;
37539         }
37540         var nb = false;
37541         
37542         if ( region == 'center') {
37543             Roo.log("Center: " + cfg.title);
37544         }
37545         
37546         
37547         switch(cfg.xtype) 
37548         {
37549             case 'Content':  // ContentPanel (el, cfg)
37550             case 'Scroll':  // ContentPanel (el, cfg)
37551             case 'View': 
37552                 cfg.autoCreate = cfg.autoCreate || true;
37553                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37554                 //} else {
37555                 //    var el = this.el.createChild();
37556                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37557                 //}
37558                 
37559                 this.add(region, ret);
37560                 break;
37561             
37562             /*
37563             case 'TreePanel': // our new panel!
37564                 cfg.el = this.el.createChild();
37565                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37566                 this.add(region, ret);
37567                 break;
37568             */
37569             
37570             case 'Nest': 
37571                 // create a new Layout (which is  a Border Layout...
37572                 
37573                 var clayout = cfg.layout;
37574                 clayout.el  = this.el.createChild();
37575                 clayout.items   = clayout.items  || [];
37576                 
37577                 delete cfg.layout;
37578                 
37579                 // replace this exitems with the clayout ones..
37580                 xitems = clayout.items;
37581                  
37582                 // force background off if it's in center...
37583                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37584                     cfg.background = false;
37585                 }
37586                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37587                 
37588                 
37589                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37590                 //console.log('adding nested layout panel '  + cfg.toSource());
37591                 this.add(region, ret);
37592                 nb = {}; /// find first...
37593                 break;
37594             
37595             case 'Grid':
37596                 
37597                 // needs grid and region
37598                 
37599                 //var el = this.getRegion(region).el.createChild();
37600                 /*
37601                  *var el = this.el.createChild();
37602                 // create the grid first...
37603                 cfg.grid.container = el;
37604                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37605                 */
37606                 
37607                 if (region == 'center' && this.active ) {
37608                     cfg.background = false;
37609                 }
37610                 
37611                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37612                 
37613                 this.add(region, ret);
37614                 /*
37615                 if (cfg.background) {
37616                     // render grid on panel activation (if panel background)
37617                     ret.on('activate', function(gp) {
37618                         if (!gp.grid.rendered) {
37619                     //        gp.grid.render(el);
37620                         }
37621                     });
37622                 } else {
37623                   //  cfg.grid.render(el);
37624                 }
37625                 */
37626                 break;
37627            
37628            
37629             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37630                 // it was the old xcomponent building that caused this before.
37631                 // espeically if border is the top element in the tree.
37632                 ret = this;
37633                 break; 
37634                 
37635                     
37636                 
37637                 
37638                 
37639             default:
37640                 /*
37641                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37642                     
37643                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37644                     this.add(region, ret);
37645                 } else {
37646                 */
37647                     Roo.log(cfg);
37648                     throw "Can not add '" + cfg.xtype + "' to Border";
37649                     return null;
37650              
37651                                 
37652              
37653         }
37654         this.beginUpdate();
37655         // add children..
37656         var region = '';
37657         var abn = {};
37658         Roo.each(xitems, function(i)  {
37659             region = nb && i.region ? i.region : false;
37660             
37661             var add = ret.addxtype(i);
37662            
37663             if (region) {
37664                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37665                 if (!i.background) {
37666                     abn[region] = nb[region] ;
37667                 }
37668             }
37669             
37670         });
37671         this.endUpdate();
37672
37673         // make the last non-background panel active..
37674         //if (nb) { Roo.log(abn); }
37675         if (nb) {
37676             
37677             for(var r in abn) {
37678                 region = this.getRegion(r);
37679                 if (region) {
37680                     // tried using nb[r], but it does not work..
37681                      
37682                     region.showPanel(abn[r]);
37683                    
37684                 }
37685             }
37686         }
37687         return ret;
37688         
37689     },
37690     
37691     
37692 // private
37693     factory : function(cfg)
37694     {
37695         
37696         var validRegions = Roo.bootstrap.layout.Border.regions;
37697
37698         var target = cfg.region;
37699         cfg.mgr = this;
37700         
37701         var r = Roo.bootstrap.layout;
37702         Roo.log(target);
37703         switch(target){
37704             case "north":
37705                 return new r.North(cfg);
37706             case "south":
37707                 return new r.South(cfg);
37708             case "east":
37709                 return new r.East(cfg);
37710             case "west":
37711                 return new r.West(cfg);
37712             case "center":
37713                 return new r.Center(cfg);
37714         }
37715         throw 'Layout region "'+target+'" not supported.';
37716     }
37717     
37718     
37719 });
37720  /*
37721  * Based on:
37722  * Ext JS Library 1.1.1
37723  * Copyright(c) 2006-2007, Ext JS, LLC.
37724  *
37725  * Originally Released Under LGPL - original licence link has changed is not relivant.
37726  *
37727  * Fork - LGPL
37728  * <script type="text/javascript">
37729  */
37730  
37731 /**
37732  * @class Roo.bootstrap.layout.Basic
37733  * @extends Roo.util.Observable
37734  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37735  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37736  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37737  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37738  * @cfg {string}   region  the region that it inhabits..
37739  * @cfg {bool}   skipConfig skip config?
37740  * 
37741
37742  */
37743 Roo.bootstrap.layout.Basic = function(config){
37744     
37745     this.mgr = config.mgr;
37746     
37747     this.position = config.region;
37748     
37749     var skipConfig = config.skipConfig;
37750     
37751     this.events = {
37752         /**
37753          * @scope Roo.BasicLayoutRegion
37754          */
37755         
37756         /**
37757          * @event beforeremove
37758          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37759          * @param {Roo.LayoutRegion} this
37760          * @param {Roo.ContentPanel} panel The panel
37761          * @param {Object} e The cancel event object
37762          */
37763         "beforeremove" : true,
37764         /**
37765          * @event invalidated
37766          * Fires when the layout for this region is changed.
37767          * @param {Roo.LayoutRegion} this
37768          */
37769         "invalidated" : true,
37770         /**
37771          * @event visibilitychange
37772          * Fires when this region is shown or hidden 
37773          * @param {Roo.LayoutRegion} this
37774          * @param {Boolean} visibility true or false
37775          */
37776         "visibilitychange" : true,
37777         /**
37778          * @event paneladded
37779          * Fires when a panel is added. 
37780          * @param {Roo.LayoutRegion} this
37781          * @param {Roo.ContentPanel} panel The panel
37782          */
37783         "paneladded" : true,
37784         /**
37785          * @event panelremoved
37786          * Fires when a panel is removed. 
37787          * @param {Roo.LayoutRegion} this
37788          * @param {Roo.ContentPanel} panel The panel
37789          */
37790         "panelremoved" : true,
37791         /**
37792          * @event beforecollapse
37793          * Fires when this region before collapse.
37794          * @param {Roo.LayoutRegion} this
37795          */
37796         "beforecollapse" : true,
37797         /**
37798          * @event collapsed
37799          * Fires when this region is collapsed.
37800          * @param {Roo.LayoutRegion} this
37801          */
37802         "collapsed" : true,
37803         /**
37804          * @event expanded
37805          * Fires when this region is expanded.
37806          * @param {Roo.LayoutRegion} this
37807          */
37808         "expanded" : true,
37809         /**
37810          * @event slideshow
37811          * Fires when this region is slid into view.
37812          * @param {Roo.LayoutRegion} this
37813          */
37814         "slideshow" : true,
37815         /**
37816          * @event slidehide
37817          * Fires when this region slides out of view. 
37818          * @param {Roo.LayoutRegion} this
37819          */
37820         "slidehide" : true,
37821         /**
37822          * @event panelactivated
37823          * Fires when a panel is activated. 
37824          * @param {Roo.LayoutRegion} this
37825          * @param {Roo.ContentPanel} panel The activated panel
37826          */
37827         "panelactivated" : true,
37828         /**
37829          * @event resized
37830          * Fires when the user resizes this region. 
37831          * @param {Roo.LayoutRegion} this
37832          * @param {Number} newSize The new size (width for east/west, height for north/south)
37833          */
37834         "resized" : true
37835     };
37836     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37837     this.panels = new Roo.util.MixedCollection();
37838     this.panels.getKey = this.getPanelId.createDelegate(this);
37839     this.box = null;
37840     this.activePanel = null;
37841     // ensure listeners are added...
37842     
37843     if (config.listeners || config.events) {
37844         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37845             listeners : config.listeners || {},
37846             events : config.events || {}
37847         });
37848     }
37849     
37850     if(skipConfig !== true){
37851         this.applyConfig(config);
37852     }
37853 };
37854
37855 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37856 {
37857     getPanelId : function(p){
37858         return p.getId();
37859     },
37860     
37861     applyConfig : function(config){
37862         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37863         this.config = config;
37864         
37865     },
37866     
37867     /**
37868      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37869      * the width, for horizontal (north, south) the height.
37870      * @param {Number} newSize The new width or height
37871      */
37872     resizeTo : function(newSize){
37873         var el = this.el ? this.el :
37874                  (this.activePanel ? this.activePanel.getEl() : null);
37875         if(el){
37876             switch(this.position){
37877                 case "east":
37878                 case "west":
37879                     el.setWidth(newSize);
37880                     this.fireEvent("resized", this, newSize);
37881                 break;
37882                 case "north":
37883                 case "south":
37884                     el.setHeight(newSize);
37885                     this.fireEvent("resized", this, newSize);
37886                 break;                
37887             }
37888         }
37889     },
37890     
37891     getBox : function(){
37892         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37893     },
37894     
37895     getMargins : function(){
37896         return this.margins;
37897     },
37898     
37899     updateBox : function(box){
37900         this.box = box;
37901         var el = this.activePanel.getEl();
37902         el.dom.style.left = box.x + "px";
37903         el.dom.style.top = box.y + "px";
37904         this.activePanel.setSize(box.width, box.height);
37905     },
37906     
37907     /**
37908      * Returns the container element for this region.
37909      * @return {Roo.Element}
37910      */
37911     getEl : function(){
37912         return this.activePanel;
37913     },
37914     
37915     /**
37916      * Returns true if this region is currently visible.
37917      * @return {Boolean}
37918      */
37919     isVisible : function(){
37920         return this.activePanel ? true : false;
37921     },
37922     
37923     setActivePanel : function(panel){
37924         panel = this.getPanel(panel);
37925         if(this.activePanel && this.activePanel != panel){
37926             this.activePanel.setActiveState(false);
37927             this.activePanel.getEl().setLeftTop(-10000,-10000);
37928         }
37929         this.activePanel = panel;
37930         panel.setActiveState(true);
37931         if(this.box){
37932             panel.setSize(this.box.width, this.box.height);
37933         }
37934         this.fireEvent("panelactivated", this, panel);
37935         this.fireEvent("invalidated");
37936     },
37937     
37938     /**
37939      * Show the specified panel.
37940      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37941      * @return {Roo.ContentPanel} The shown panel or null
37942      */
37943     showPanel : function(panel){
37944         panel = this.getPanel(panel);
37945         if(panel){
37946             this.setActivePanel(panel);
37947         }
37948         return panel;
37949     },
37950     
37951     /**
37952      * Get the active panel for this region.
37953      * @return {Roo.ContentPanel} The active panel or null
37954      */
37955     getActivePanel : function(){
37956         return this.activePanel;
37957     },
37958     
37959     /**
37960      * Add the passed ContentPanel(s)
37961      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37962      * @return {Roo.ContentPanel} The panel added (if only one was added)
37963      */
37964     add : function(panel){
37965         if(arguments.length > 1){
37966             for(var i = 0, len = arguments.length; i < len; i++) {
37967                 this.add(arguments[i]);
37968             }
37969             return null;
37970         }
37971         if(this.hasPanel(panel)){
37972             this.showPanel(panel);
37973             return panel;
37974         }
37975         var el = panel.getEl();
37976         if(el.dom.parentNode != this.mgr.el.dom){
37977             this.mgr.el.dom.appendChild(el.dom);
37978         }
37979         if(panel.setRegion){
37980             panel.setRegion(this);
37981         }
37982         this.panels.add(panel);
37983         el.setStyle("position", "absolute");
37984         if(!panel.background){
37985             this.setActivePanel(panel);
37986             if(this.config.initialSize && this.panels.getCount()==1){
37987                 this.resizeTo(this.config.initialSize);
37988             }
37989         }
37990         this.fireEvent("paneladded", this, panel);
37991         return panel;
37992     },
37993     
37994     /**
37995      * Returns true if the panel is in this region.
37996      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37997      * @return {Boolean}
37998      */
37999     hasPanel : function(panel){
38000         if(typeof panel == "object"){ // must be panel obj
38001             panel = panel.getId();
38002         }
38003         return this.getPanel(panel) ? true : false;
38004     },
38005     
38006     /**
38007      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38008      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38009      * @param {Boolean} preservePanel Overrides the config preservePanel option
38010      * @return {Roo.ContentPanel} The panel that was removed
38011      */
38012     remove : function(panel, preservePanel){
38013         panel = this.getPanel(panel);
38014         if(!panel){
38015             return null;
38016         }
38017         var e = {};
38018         this.fireEvent("beforeremove", this, panel, e);
38019         if(e.cancel === true){
38020             return null;
38021         }
38022         var panelId = panel.getId();
38023         this.panels.removeKey(panelId);
38024         return panel;
38025     },
38026     
38027     /**
38028      * Returns the panel specified or null if it's not in this region.
38029      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38030      * @return {Roo.ContentPanel}
38031      */
38032     getPanel : function(id){
38033         if(typeof id == "object"){ // must be panel obj
38034             return id;
38035         }
38036         return this.panels.get(id);
38037     },
38038     
38039     /**
38040      * Returns this regions position (north/south/east/west/center).
38041      * @return {String} 
38042      */
38043     getPosition: function(){
38044         return this.position;    
38045     }
38046 });/*
38047  * Based on:
38048  * Ext JS Library 1.1.1
38049  * Copyright(c) 2006-2007, Ext JS, LLC.
38050  *
38051  * Originally Released Under LGPL - original licence link has changed is not relivant.
38052  *
38053  * Fork - LGPL
38054  * <script type="text/javascript">
38055  */
38056  
38057 /**
38058  * @class Roo.bootstrap.layout.Region
38059  * @extends Roo.bootstrap.layout.Basic
38060  * This class represents a region in a layout manager.
38061  
38062  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38063  * @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})
38064  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38065  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38066  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38067  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38068  * @cfg {String}    title           The title for the region (overrides panel titles)
38069  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38070  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38071  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38072  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38073  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38074  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38075  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38076  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38077  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38078  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38079
38080  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38081  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38082  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38083  * @cfg {Number}    width           For East/West panels
38084  * @cfg {Number}    height          For North/South panels
38085  * @cfg {Boolean}   split           To show the splitter
38086  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38087  * 
38088  * @cfg {string}   cls             Extra CSS classes to add to region
38089  * 
38090  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38091  * @cfg {string}   region  the region that it inhabits..
38092  *
38093
38094  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38095  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38096
38097  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38098  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38099  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38100  */
38101 Roo.bootstrap.layout.Region = function(config)
38102 {
38103     this.applyConfig(config);
38104
38105     var mgr = config.mgr;
38106     var pos = config.region;
38107     config.skipConfig = true;
38108     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38109     
38110     if (mgr.el) {
38111         this.onRender(mgr.el);   
38112     }
38113      
38114     this.visible = true;
38115     this.collapsed = false;
38116     this.unrendered_panels = [];
38117 };
38118
38119 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38120
38121     position: '', // set by wrapper (eg. north/south etc..)
38122     unrendered_panels : null,  // unrendered panels.
38123     
38124     tabPosition : false,
38125     
38126     mgr: false, // points to 'Border'
38127     
38128     
38129     createBody : function(){
38130         /** This region's body element 
38131         * @type Roo.Element */
38132         this.bodyEl = this.el.createChild({
38133                 tag: "div",
38134                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38135         });
38136     },
38137
38138     onRender: function(ctr, pos)
38139     {
38140         var dh = Roo.DomHelper;
38141         /** This region's container element 
38142         * @type Roo.Element */
38143         this.el = dh.append(ctr.dom, {
38144                 tag: "div",
38145                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38146             }, true);
38147         /** This region's title element 
38148         * @type Roo.Element */
38149     
38150         this.titleEl = dh.append(this.el.dom,  {
38151                 tag: "div",
38152                 unselectable: "on",
38153                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38154                 children:[
38155                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38156                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38157                 ]
38158             }, true);
38159         
38160         this.titleEl.enableDisplayMode();
38161         /** This region's title text element 
38162         * @type HTMLElement */
38163         this.titleTextEl = this.titleEl.dom.firstChild;
38164         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38165         /*
38166         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38167         this.closeBtn.enableDisplayMode();
38168         this.closeBtn.on("click", this.closeClicked, this);
38169         this.closeBtn.hide();
38170     */
38171         this.createBody(this.config);
38172         if(this.config.hideWhenEmpty){
38173             this.hide();
38174             this.on("paneladded", this.validateVisibility, this);
38175             this.on("panelremoved", this.validateVisibility, this);
38176         }
38177         if(this.autoScroll){
38178             this.bodyEl.setStyle("overflow", "auto");
38179         }else{
38180             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38181         }
38182         //if(c.titlebar !== false){
38183             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38184                 this.titleEl.hide();
38185             }else{
38186                 this.titleEl.show();
38187                 if(this.config.title){
38188                     this.titleTextEl.innerHTML = this.config.title;
38189                 }
38190             }
38191         //}
38192         if(this.config.collapsed){
38193             this.collapse(true);
38194         }
38195         if(this.config.hidden){
38196             this.hide();
38197         }
38198         
38199         if (this.unrendered_panels && this.unrendered_panels.length) {
38200             for (var i =0;i< this.unrendered_panels.length; i++) {
38201                 this.add(this.unrendered_panels[i]);
38202             }
38203             this.unrendered_panels = null;
38204             
38205         }
38206         
38207     },
38208     
38209     applyConfig : function(c)
38210     {
38211         /*
38212          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38213             var dh = Roo.DomHelper;
38214             if(c.titlebar !== false){
38215                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38216                 this.collapseBtn.on("click", this.collapse, this);
38217                 this.collapseBtn.enableDisplayMode();
38218                 /*
38219                 if(c.showPin === true || this.showPin){
38220                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38221                     this.stickBtn.enableDisplayMode();
38222                     this.stickBtn.on("click", this.expand, this);
38223                     this.stickBtn.hide();
38224                 }
38225                 
38226             }
38227             */
38228             /** This region's collapsed element
38229             * @type Roo.Element */
38230             /*
38231              *
38232             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38233                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38234             ]}, true);
38235             
38236             if(c.floatable !== false){
38237                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38238                this.collapsedEl.on("click", this.collapseClick, this);
38239             }
38240
38241             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38242                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38243                    id: "message", unselectable: "on", style:{"float":"left"}});
38244                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38245              }
38246             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38247             this.expandBtn.on("click", this.expand, this);
38248             
38249         }
38250         
38251         if(this.collapseBtn){
38252             this.collapseBtn.setVisible(c.collapsible == true);
38253         }
38254         
38255         this.cmargins = c.cmargins || this.cmargins ||
38256                          (this.position == "west" || this.position == "east" ?
38257                              {top: 0, left: 2, right:2, bottom: 0} :
38258                              {top: 2, left: 0, right:0, bottom: 2});
38259         */
38260         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38261         
38262         
38263         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38264         
38265         this.autoScroll = c.autoScroll || false;
38266         
38267         
38268        
38269         
38270         this.duration = c.duration || .30;
38271         this.slideDuration = c.slideDuration || .45;
38272         this.config = c;
38273        
38274     },
38275     /**
38276      * Returns true if this region is currently visible.
38277      * @return {Boolean}
38278      */
38279     isVisible : function(){
38280         return this.visible;
38281     },
38282
38283     /**
38284      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38285      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38286      */
38287     //setCollapsedTitle : function(title){
38288     //    title = title || "&#160;";
38289      //   if(this.collapsedTitleTextEl){
38290       //      this.collapsedTitleTextEl.innerHTML = title;
38291        // }
38292     //},
38293
38294     getBox : function(){
38295         var b;
38296       //  if(!this.collapsed){
38297             b = this.el.getBox(false, true);
38298        // }else{
38299           //  b = this.collapsedEl.getBox(false, true);
38300         //}
38301         return b;
38302     },
38303
38304     getMargins : function(){
38305         return this.margins;
38306         //return this.collapsed ? this.cmargins : this.margins;
38307     },
38308 /*
38309     highlight : function(){
38310         this.el.addClass("x-layout-panel-dragover");
38311     },
38312
38313     unhighlight : function(){
38314         this.el.removeClass("x-layout-panel-dragover");
38315     },
38316 */
38317     updateBox : function(box)
38318     {
38319         if (!this.bodyEl) {
38320             return; // not rendered yet..
38321         }
38322         
38323         this.box = box;
38324         if(!this.collapsed){
38325             this.el.dom.style.left = box.x + "px";
38326             this.el.dom.style.top = box.y + "px";
38327             this.updateBody(box.width, box.height);
38328         }else{
38329             this.collapsedEl.dom.style.left = box.x + "px";
38330             this.collapsedEl.dom.style.top = box.y + "px";
38331             this.collapsedEl.setSize(box.width, box.height);
38332         }
38333         if(this.tabs){
38334             this.tabs.autoSizeTabs();
38335         }
38336     },
38337
38338     updateBody : function(w, h)
38339     {
38340         if(w !== null){
38341             this.el.setWidth(w);
38342             w -= this.el.getBorderWidth("rl");
38343             if(this.config.adjustments){
38344                 w += this.config.adjustments[0];
38345             }
38346         }
38347         if(h !== null && h > 0){
38348             this.el.setHeight(h);
38349             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38350             h -= this.el.getBorderWidth("tb");
38351             if(this.config.adjustments){
38352                 h += this.config.adjustments[1];
38353             }
38354             this.bodyEl.setHeight(h);
38355             if(this.tabs){
38356                 h = this.tabs.syncHeight(h);
38357             }
38358         }
38359         if(this.panelSize){
38360             w = w !== null ? w : this.panelSize.width;
38361             h = h !== null ? h : this.panelSize.height;
38362         }
38363         if(this.activePanel){
38364             var el = this.activePanel.getEl();
38365             w = w !== null ? w : el.getWidth();
38366             h = h !== null ? h : el.getHeight();
38367             this.panelSize = {width: w, height: h};
38368             this.activePanel.setSize(w, h);
38369         }
38370         if(Roo.isIE && this.tabs){
38371             this.tabs.el.repaint();
38372         }
38373     },
38374
38375     /**
38376      * Returns the container element for this region.
38377      * @return {Roo.Element}
38378      */
38379     getEl : function(){
38380         return this.el;
38381     },
38382
38383     /**
38384      * Hides this region.
38385      */
38386     hide : function(){
38387         //if(!this.collapsed){
38388             this.el.dom.style.left = "-2000px";
38389             this.el.hide();
38390         //}else{
38391          //   this.collapsedEl.dom.style.left = "-2000px";
38392          //   this.collapsedEl.hide();
38393        // }
38394         this.visible = false;
38395         this.fireEvent("visibilitychange", this, false);
38396     },
38397
38398     /**
38399      * Shows this region if it was previously hidden.
38400      */
38401     show : function(){
38402         //if(!this.collapsed){
38403             this.el.show();
38404         //}else{
38405         //    this.collapsedEl.show();
38406        // }
38407         this.visible = true;
38408         this.fireEvent("visibilitychange", this, true);
38409     },
38410 /*
38411     closeClicked : function(){
38412         if(this.activePanel){
38413             this.remove(this.activePanel);
38414         }
38415     },
38416
38417     collapseClick : function(e){
38418         if(this.isSlid){
38419            e.stopPropagation();
38420            this.slideIn();
38421         }else{
38422            e.stopPropagation();
38423            this.slideOut();
38424         }
38425     },
38426 */
38427     /**
38428      * Collapses this region.
38429      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38430      */
38431     /*
38432     collapse : function(skipAnim, skipCheck = false){
38433         if(this.collapsed) {
38434             return;
38435         }
38436         
38437         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38438             
38439             this.collapsed = true;
38440             if(this.split){
38441                 this.split.el.hide();
38442             }
38443             if(this.config.animate && skipAnim !== true){
38444                 this.fireEvent("invalidated", this);
38445                 this.animateCollapse();
38446             }else{
38447                 this.el.setLocation(-20000,-20000);
38448                 this.el.hide();
38449                 this.collapsedEl.show();
38450                 this.fireEvent("collapsed", this);
38451                 this.fireEvent("invalidated", this);
38452             }
38453         }
38454         
38455     },
38456 */
38457     animateCollapse : function(){
38458         // overridden
38459     },
38460
38461     /**
38462      * Expands this region if it was previously collapsed.
38463      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38464      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38465      */
38466     /*
38467     expand : function(e, skipAnim){
38468         if(e) {
38469             e.stopPropagation();
38470         }
38471         if(!this.collapsed || this.el.hasActiveFx()) {
38472             return;
38473         }
38474         if(this.isSlid){
38475             this.afterSlideIn();
38476             skipAnim = true;
38477         }
38478         this.collapsed = false;
38479         if(this.config.animate && skipAnim !== true){
38480             this.animateExpand();
38481         }else{
38482             this.el.show();
38483             if(this.split){
38484                 this.split.el.show();
38485             }
38486             this.collapsedEl.setLocation(-2000,-2000);
38487             this.collapsedEl.hide();
38488             this.fireEvent("invalidated", this);
38489             this.fireEvent("expanded", this);
38490         }
38491     },
38492 */
38493     animateExpand : function(){
38494         // overridden
38495     },
38496
38497     initTabs : function()
38498     {
38499         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38500         
38501         var ts = new Roo.bootstrap.panel.Tabs({
38502             el: this.bodyEl.dom,
38503             region : this,
38504             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38505             disableTooltips: this.config.disableTabTips,
38506             toolbar : this.config.toolbar
38507         });
38508         
38509         if(this.config.hideTabs){
38510             ts.stripWrap.setDisplayed(false);
38511         }
38512         this.tabs = ts;
38513         ts.resizeTabs = this.config.resizeTabs === true;
38514         ts.minTabWidth = this.config.minTabWidth || 40;
38515         ts.maxTabWidth = this.config.maxTabWidth || 250;
38516         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38517         ts.monitorResize = false;
38518         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38519         ts.bodyEl.addClass('roo-layout-tabs-body');
38520         this.panels.each(this.initPanelAsTab, this);
38521     },
38522
38523     initPanelAsTab : function(panel){
38524         var ti = this.tabs.addTab(
38525             panel.getEl().id,
38526             panel.getTitle(),
38527             null,
38528             this.config.closeOnTab && panel.isClosable(),
38529             panel.tpl
38530         );
38531         if(panel.tabTip !== undefined){
38532             ti.setTooltip(panel.tabTip);
38533         }
38534         ti.on("activate", function(){
38535               this.setActivePanel(panel);
38536         }, this);
38537         
38538         if(this.config.closeOnTab){
38539             ti.on("beforeclose", function(t, e){
38540                 e.cancel = true;
38541                 this.remove(panel);
38542             }, this);
38543         }
38544         
38545         panel.tabItem = ti;
38546         
38547         return ti;
38548     },
38549
38550     updatePanelTitle : function(panel, title)
38551     {
38552         if(this.activePanel == panel){
38553             this.updateTitle(title);
38554         }
38555         if(this.tabs){
38556             var ti = this.tabs.getTab(panel.getEl().id);
38557             ti.setText(title);
38558             if(panel.tabTip !== undefined){
38559                 ti.setTooltip(panel.tabTip);
38560             }
38561         }
38562     },
38563
38564     updateTitle : function(title){
38565         if(this.titleTextEl && !this.config.title){
38566             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38567         }
38568     },
38569
38570     setActivePanel : function(panel)
38571     {
38572         panel = this.getPanel(panel);
38573         if(this.activePanel && this.activePanel != panel){
38574             if(this.activePanel.setActiveState(false) === false){
38575                 return;
38576             }
38577         }
38578         this.activePanel = panel;
38579         panel.setActiveState(true);
38580         if(this.panelSize){
38581             panel.setSize(this.panelSize.width, this.panelSize.height);
38582         }
38583         if(this.closeBtn){
38584             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38585         }
38586         this.updateTitle(panel.getTitle());
38587         if(this.tabs){
38588             this.fireEvent("invalidated", this);
38589         }
38590         this.fireEvent("panelactivated", this, panel);
38591     },
38592
38593     /**
38594      * Shows the specified panel.
38595      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38596      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38597      */
38598     showPanel : function(panel)
38599     {
38600         panel = this.getPanel(panel);
38601         if(panel){
38602             if(this.tabs){
38603                 var tab = this.tabs.getTab(panel.getEl().id);
38604                 if(tab.isHidden()){
38605                     this.tabs.unhideTab(tab.id);
38606                 }
38607                 tab.activate();
38608             }else{
38609                 this.setActivePanel(panel);
38610             }
38611         }
38612         return panel;
38613     },
38614
38615     /**
38616      * Get the active panel for this region.
38617      * @return {Roo.ContentPanel} The active panel or null
38618      */
38619     getActivePanel : function(){
38620         return this.activePanel;
38621     },
38622
38623     validateVisibility : function(){
38624         if(this.panels.getCount() < 1){
38625             this.updateTitle("&#160;");
38626             this.closeBtn.hide();
38627             this.hide();
38628         }else{
38629             if(!this.isVisible()){
38630                 this.show();
38631             }
38632         }
38633     },
38634
38635     /**
38636      * Adds the passed ContentPanel(s) to this region.
38637      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38638      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38639      */
38640     add : function(panel)
38641     {
38642         if(arguments.length > 1){
38643             for(var i = 0, len = arguments.length; i < len; i++) {
38644                 this.add(arguments[i]);
38645             }
38646             return null;
38647         }
38648         
38649         // if we have not been rendered yet, then we can not really do much of this..
38650         if (!this.bodyEl) {
38651             this.unrendered_panels.push(panel);
38652             return panel;
38653         }
38654         
38655         
38656         
38657         
38658         if(this.hasPanel(panel)){
38659             this.showPanel(panel);
38660             return panel;
38661         }
38662         panel.setRegion(this);
38663         this.panels.add(panel);
38664        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38665             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38666             // and hide them... ???
38667             this.bodyEl.dom.appendChild(panel.getEl().dom);
38668             if(panel.background !== true){
38669                 this.setActivePanel(panel);
38670             }
38671             this.fireEvent("paneladded", this, panel);
38672             return panel;
38673         }
38674         */
38675         if(!this.tabs){
38676             this.initTabs();
38677         }else{
38678             this.initPanelAsTab(panel);
38679         }
38680         
38681         
38682         if(panel.background !== true){
38683             this.tabs.activate(panel.getEl().id);
38684         }
38685         this.fireEvent("paneladded", this, panel);
38686         return panel;
38687     },
38688
38689     /**
38690      * Hides the tab for the specified panel.
38691      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38692      */
38693     hidePanel : function(panel){
38694         if(this.tabs && (panel = this.getPanel(panel))){
38695             this.tabs.hideTab(panel.getEl().id);
38696         }
38697     },
38698
38699     /**
38700      * Unhides the tab for a previously hidden panel.
38701      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38702      */
38703     unhidePanel : function(panel){
38704         if(this.tabs && (panel = this.getPanel(panel))){
38705             this.tabs.unhideTab(panel.getEl().id);
38706         }
38707     },
38708
38709     clearPanels : function(){
38710         while(this.panels.getCount() > 0){
38711              this.remove(this.panels.first());
38712         }
38713     },
38714
38715     /**
38716      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38717      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38718      * @param {Boolean} preservePanel Overrides the config preservePanel option
38719      * @return {Roo.ContentPanel} The panel that was removed
38720      */
38721     remove : function(panel, preservePanel)
38722     {
38723         panel = this.getPanel(panel);
38724         if(!panel){
38725             return null;
38726         }
38727         var e = {};
38728         this.fireEvent("beforeremove", this, panel, e);
38729         if(e.cancel === true){
38730             return null;
38731         }
38732         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38733         var panelId = panel.getId();
38734         this.panels.removeKey(panelId);
38735         if(preservePanel){
38736             document.body.appendChild(panel.getEl().dom);
38737         }
38738         if(this.tabs){
38739             this.tabs.removeTab(panel.getEl().id);
38740         }else if (!preservePanel){
38741             this.bodyEl.dom.removeChild(panel.getEl().dom);
38742         }
38743         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38744             var p = this.panels.first();
38745             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38746             tempEl.appendChild(p.getEl().dom);
38747             this.bodyEl.update("");
38748             this.bodyEl.dom.appendChild(p.getEl().dom);
38749             tempEl = null;
38750             this.updateTitle(p.getTitle());
38751             this.tabs = null;
38752             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38753             this.setActivePanel(p);
38754         }
38755         panel.setRegion(null);
38756         if(this.activePanel == panel){
38757             this.activePanel = null;
38758         }
38759         if(this.config.autoDestroy !== false && preservePanel !== true){
38760             try{panel.destroy();}catch(e){}
38761         }
38762         this.fireEvent("panelremoved", this, panel);
38763         return panel;
38764     },
38765
38766     /**
38767      * Returns the TabPanel component used by this region
38768      * @return {Roo.TabPanel}
38769      */
38770     getTabs : function(){
38771         return this.tabs;
38772     },
38773
38774     createTool : function(parentEl, className){
38775         var btn = Roo.DomHelper.append(parentEl, {
38776             tag: "div",
38777             cls: "x-layout-tools-button",
38778             children: [ {
38779                 tag: "div",
38780                 cls: "roo-layout-tools-button-inner " + className,
38781                 html: "&#160;"
38782             }]
38783         }, true);
38784         btn.addClassOnOver("roo-layout-tools-button-over");
38785         return btn;
38786     }
38787 });/*
38788  * Based on:
38789  * Ext JS Library 1.1.1
38790  * Copyright(c) 2006-2007, Ext JS, LLC.
38791  *
38792  * Originally Released Under LGPL - original licence link has changed is not relivant.
38793  *
38794  * Fork - LGPL
38795  * <script type="text/javascript">
38796  */
38797  
38798
38799
38800 /**
38801  * @class Roo.SplitLayoutRegion
38802  * @extends Roo.LayoutRegion
38803  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38804  */
38805 Roo.bootstrap.layout.Split = function(config){
38806     this.cursor = config.cursor;
38807     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38808 };
38809
38810 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38811 {
38812     splitTip : "Drag to resize.",
38813     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38814     useSplitTips : false,
38815
38816     applyConfig : function(config){
38817         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38818     },
38819     
38820     onRender : function(ctr,pos) {
38821         
38822         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38823         if(!this.config.split){
38824             return;
38825         }
38826         if(!this.split){
38827             
38828             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38829                             tag: "div",
38830                             id: this.el.id + "-split",
38831                             cls: "roo-layout-split roo-layout-split-"+this.position,
38832                             html: "&#160;"
38833             });
38834             /** The SplitBar for this region 
38835             * @type Roo.SplitBar */
38836             // does not exist yet...
38837             Roo.log([this.position, this.orientation]);
38838             
38839             this.split = new Roo.bootstrap.SplitBar({
38840                 dragElement : splitEl,
38841                 resizingElement: this.el,
38842                 orientation : this.orientation
38843             });
38844             
38845             this.split.on("moved", this.onSplitMove, this);
38846             this.split.useShim = this.config.useShim === true;
38847             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38848             if(this.useSplitTips){
38849                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38850             }
38851             //if(config.collapsible){
38852             //    this.split.el.on("dblclick", this.collapse,  this);
38853             //}
38854         }
38855         if(typeof this.config.minSize != "undefined"){
38856             this.split.minSize = this.config.minSize;
38857         }
38858         if(typeof this.config.maxSize != "undefined"){
38859             this.split.maxSize = this.config.maxSize;
38860         }
38861         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38862             this.hideSplitter();
38863         }
38864         
38865     },
38866
38867     getHMaxSize : function(){
38868          var cmax = this.config.maxSize || 10000;
38869          var center = this.mgr.getRegion("center");
38870          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38871     },
38872
38873     getVMaxSize : function(){
38874          var cmax = this.config.maxSize || 10000;
38875          var center = this.mgr.getRegion("center");
38876          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38877     },
38878
38879     onSplitMove : function(split, newSize){
38880         this.fireEvent("resized", this, newSize);
38881     },
38882     
38883     /** 
38884      * Returns the {@link Roo.SplitBar} for this region.
38885      * @return {Roo.SplitBar}
38886      */
38887     getSplitBar : function(){
38888         return this.split;
38889     },
38890     
38891     hide : function(){
38892         this.hideSplitter();
38893         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38894     },
38895
38896     hideSplitter : function(){
38897         if(this.split){
38898             this.split.el.setLocation(-2000,-2000);
38899             this.split.el.hide();
38900         }
38901     },
38902
38903     show : function(){
38904         if(this.split){
38905             this.split.el.show();
38906         }
38907         Roo.bootstrap.layout.Split.superclass.show.call(this);
38908     },
38909     
38910     beforeSlide: function(){
38911         if(Roo.isGecko){// firefox overflow auto bug workaround
38912             this.bodyEl.clip();
38913             if(this.tabs) {
38914                 this.tabs.bodyEl.clip();
38915             }
38916             if(this.activePanel){
38917                 this.activePanel.getEl().clip();
38918                 
38919                 if(this.activePanel.beforeSlide){
38920                     this.activePanel.beforeSlide();
38921                 }
38922             }
38923         }
38924     },
38925     
38926     afterSlide : function(){
38927         if(Roo.isGecko){// firefox overflow auto bug workaround
38928             this.bodyEl.unclip();
38929             if(this.tabs) {
38930                 this.tabs.bodyEl.unclip();
38931             }
38932             if(this.activePanel){
38933                 this.activePanel.getEl().unclip();
38934                 if(this.activePanel.afterSlide){
38935                     this.activePanel.afterSlide();
38936                 }
38937             }
38938         }
38939     },
38940
38941     initAutoHide : function(){
38942         if(this.autoHide !== false){
38943             if(!this.autoHideHd){
38944                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38945                 this.autoHideHd = {
38946                     "mouseout": function(e){
38947                         if(!e.within(this.el, true)){
38948                             st.delay(500);
38949                         }
38950                     },
38951                     "mouseover" : function(e){
38952                         st.cancel();
38953                     },
38954                     scope : this
38955                 };
38956             }
38957             this.el.on(this.autoHideHd);
38958         }
38959     },
38960
38961     clearAutoHide : function(){
38962         if(this.autoHide !== false){
38963             this.el.un("mouseout", this.autoHideHd.mouseout);
38964             this.el.un("mouseover", this.autoHideHd.mouseover);
38965         }
38966     },
38967
38968     clearMonitor : function(){
38969         Roo.get(document).un("click", this.slideInIf, this);
38970     },
38971
38972     // these names are backwards but not changed for compat
38973     slideOut : function(){
38974         if(this.isSlid || this.el.hasActiveFx()){
38975             return;
38976         }
38977         this.isSlid = true;
38978         if(this.collapseBtn){
38979             this.collapseBtn.hide();
38980         }
38981         this.closeBtnState = this.closeBtn.getStyle('display');
38982         this.closeBtn.hide();
38983         if(this.stickBtn){
38984             this.stickBtn.show();
38985         }
38986         this.el.show();
38987         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38988         this.beforeSlide();
38989         this.el.setStyle("z-index", 10001);
38990         this.el.slideIn(this.getSlideAnchor(), {
38991             callback: function(){
38992                 this.afterSlide();
38993                 this.initAutoHide();
38994                 Roo.get(document).on("click", this.slideInIf, this);
38995                 this.fireEvent("slideshow", this);
38996             },
38997             scope: this,
38998             block: true
38999         });
39000     },
39001
39002     afterSlideIn : function(){
39003         this.clearAutoHide();
39004         this.isSlid = false;
39005         this.clearMonitor();
39006         this.el.setStyle("z-index", "");
39007         if(this.collapseBtn){
39008             this.collapseBtn.show();
39009         }
39010         this.closeBtn.setStyle('display', this.closeBtnState);
39011         if(this.stickBtn){
39012             this.stickBtn.hide();
39013         }
39014         this.fireEvent("slidehide", this);
39015     },
39016
39017     slideIn : function(cb){
39018         if(!this.isSlid || this.el.hasActiveFx()){
39019             Roo.callback(cb);
39020             return;
39021         }
39022         this.isSlid = false;
39023         this.beforeSlide();
39024         this.el.slideOut(this.getSlideAnchor(), {
39025             callback: function(){
39026                 this.el.setLeftTop(-10000, -10000);
39027                 this.afterSlide();
39028                 this.afterSlideIn();
39029                 Roo.callback(cb);
39030             },
39031             scope: this,
39032             block: true
39033         });
39034     },
39035     
39036     slideInIf : function(e){
39037         if(!e.within(this.el)){
39038             this.slideIn();
39039         }
39040     },
39041
39042     animateCollapse : function(){
39043         this.beforeSlide();
39044         this.el.setStyle("z-index", 20000);
39045         var anchor = this.getSlideAnchor();
39046         this.el.slideOut(anchor, {
39047             callback : function(){
39048                 this.el.setStyle("z-index", "");
39049                 this.collapsedEl.slideIn(anchor, {duration:.3});
39050                 this.afterSlide();
39051                 this.el.setLocation(-10000,-10000);
39052                 this.el.hide();
39053                 this.fireEvent("collapsed", this);
39054             },
39055             scope: this,
39056             block: true
39057         });
39058     },
39059
39060     animateExpand : function(){
39061         this.beforeSlide();
39062         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39063         this.el.setStyle("z-index", 20000);
39064         this.collapsedEl.hide({
39065             duration:.1
39066         });
39067         this.el.slideIn(this.getSlideAnchor(), {
39068             callback : function(){
39069                 this.el.setStyle("z-index", "");
39070                 this.afterSlide();
39071                 if(this.split){
39072                     this.split.el.show();
39073                 }
39074                 this.fireEvent("invalidated", this);
39075                 this.fireEvent("expanded", this);
39076             },
39077             scope: this,
39078             block: true
39079         });
39080     },
39081
39082     anchors : {
39083         "west" : "left",
39084         "east" : "right",
39085         "north" : "top",
39086         "south" : "bottom"
39087     },
39088
39089     sanchors : {
39090         "west" : "l",
39091         "east" : "r",
39092         "north" : "t",
39093         "south" : "b"
39094     },
39095
39096     canchors : {
39097         "west" : "tl-tr",
39098         "east" : "tr-tl",
39099         "north" : "tl-bl",
39100         "south" : "bl-tl"
39101     },
39102
39103     getAnchor : function(){
39104         return this.anchors[this.position];
39105     },
39106
39107     getCollapseAnchor : function(){
39108         return this.canchors[this.position];
39109     },
39110
39111     getSlideAnchor : function(){
39112         return this.sanchors[this.position];
39113     },
39114
39115     getAlignAdj : function(){
39116         var cm = this.cmargins;
39117         switch(this.position){
39118             case "west":
39119                 return [0, 0];
39120             break;
39121             case "east":
39122                 return [0, 0];
39123             break;
39124             case "north":
39125                 return [0, 0];
39126             break;
39127             case "south":
39128                 return [0, 0];
39129             break;
39130         }
39131     },
39132
39133     getExpandAdj : function(){
39134         var c = this.collapsedEl, cm = this.cmargins;
39135         switch(this.position){
39136             case "west":
39137                 return [-(cm.right+c.getWidth()+cm.left), 0];
39138             break;
39139             case "east":
39140                 return [cm.right+c.getWidth()+cm.left, 0];
39141             break;
39142             case "north":
39143                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39144             break;
39145             case "south":
39146                 return [0, cm.top+cm.bottom+c.getHeight()];
39147             break;
39148         }
39149     }
39150 });/*
39151  * Based on:
39152  * Ext JS Library 1.1.1
39153  * Copyright(c) 2006-2007, Ext JS, LLC.
39154  *
39155  * Originally Released Under LGPL - original licence link has changed is not relivant.
39156  *
39157  * Fork - LGPL
39158  * <script type="text/javascript">
39159  */
39160 /*
39161  * These classes are private internal classes
39162  */
39163 Roo.bootstrap.layout.Center = function(config){
39164     config.region = "center";
39165     Roo.bootstrap.layout.Region.call(this, config);
39166     this.visible = true;
39167     this.minWidth = config.minWidth || 20;
39168     this.minHeight = config.minHeight || 20;
39169 };
39170
39171 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39172     hide : function(){
39173         // center panel can't be hidden
39174     },
39175     
39176     show : function(){
39177         // center panel can't be hidden
39178     },
39179     
39180     getMinWidth: function(){
39181         return this.minWidth;
39182     },
39183     
39184     getMinHeight: function(){
39185         return this.minHeight;
39186     }
39187 });
39188
39189
39190
39191
39192  
39193
39194
39195
39196
39197
39198
39199 Roo.bootstrap.layout.North = function(config)
39200 {
39201     config.region = 'north';
39202     config.cursor = 'n-resize';
39203     
39204     Roo.bootstrap.layout.Split.call(this, config);
39205     
39206     
39207     if(this.split){
39208         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39209         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39210         this.split.el.addClass("roo-layout-split-v");
39211     }
39212     var size = config.initialSize || config.height;
39213     if(typeof size != "undefined"){
39214         this.el.setHeight(size);
39215     }
39216 };
39217 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39218 {
39219     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39220     
39221     
39222     
39223     getBox : function(){
39224         if(this.collapsed){
39225             return this.collapsedEl.getBox();
39226         }
39227         var box = this.el.getBox();
39228         if(this.split){
39229             box.height += this.split.el.getHeight();
39230         }
39231         return box;
39232     },
39233     
39234     updateBox : function(box){
39235         if(this.split && !this.collapsed){
39236             box.height -= this.split.el.getHeight();
39237             this.split.el.setLeft(box.x);
39238             this.split.el.setTop(box.y+box.height);
39239             this.split.el.setWidth(box.width);
39240         }
39241         if(this.collapsed){
39242             this.updateBody(box.width, null);
39243         }
39244         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39245     }
39246 });
39247
39248
39249
39250
39251
39252 Roo.bootstrap.layout.South = function(config){
39253     config.region = 'south';
39254     config.cursor = 's-resize';
39255     Roo.bootstrap.layout.Split.call(this, config);
39256     if(this.split){
39257         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39258         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39259         this.split.el.addClass("roo-layout-split-v");
39260     }
39261     var size = config.initialSize || config.height;
39262     if(typeof size != "undefined"){
39263         this.el.setHeight(size);
39264     }
39265 };
39266
39267 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39268     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39269     getBox : function(){
39270         if(this.collapsed){
39271             return this.collapsedEl.getBox();
39272         }
39273         var box = this.el.getBox();
39274         if(this.split){
39275             var sh = this.split.el.getHeight();
39276             box.height += sh;
39277             box.y -= sh;
39278         }
39279         return box;
39280     },
39281     
39282     updateBox : function(box){
39283         if(this.split && !this.collapsed){
39284             var sh = this.split.el.getHeight();
39285             box.height -= sh;
39286             box.y += sh;
39287             this.split.el.setLeft(box.x);
39288             this.split.el.setTop(box.y-sh);
39289             this.split.el.setWidth(box.width);
39290         }
39291         if(this.collapsed){
39292             this.updateBody(box.width, null);
39293         }
39294         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39295     }
39296 });
39297
39298 Roo.bootstrap.layout.East = function(config){
39299     config.region = "east";
39300     config.cursor = "e-resize";
39301     Roo.bootstrap.layout.Split.call(this, config);
39302     if(this.split){
39303         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39304         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39305         this.split.el.addClass("roo-layout-split-h");
39306     }
39307     var size = config.initialSize || config.width;
39308     if(typeof size != "undefined"){
39309         this.el.setWidth(size);
39310     }
39311 };
39312 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39313     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39314     getBox : function(){
39315         if(this.collapsed){
39316             return this.collapsedEl.getBox();
39317         }
39318         var box = this.el.getBox();
39319         if(this.split){
39320             var sw = this.split.el.getWidth();
39321             box.width += sw;
39322             box.x -= sw;
39323         }
39324         return box;
39325     },
39326
39327     updateBox : function(box){
39328         if(this.split && !this.collapsed){
39329             var sw = this.split.el.getWidth();
39330             box.width -= sw;
39331             this.split.el.setLeft(box.x);
39332             this.split.el.setTop(box.y);
39333             this.split.el.setHeight(box.height);
39334             box.x += sw;
39335         }
39336         if(this.collapsed){
39337             this.updateBody(null, box.height);
39338         }
39339         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39340     }
39341 });
39342
39343 Roo.bootstrap.layout.West = function(config){
39344     config.region = "west";
39345     config.cursor = "w-resize";
39346     
39347     Roo.bootstrap.layout.Split.call(this, config);
39348     if(this.split){
39349         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39350         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39351         this.split.el.addClass("roo-layout-split-h");
39352     }
39353     
39354 };
39355 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39356     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39357     
39358     onRender: function(ctr, pos)
39359     {
39360         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39361         var size = this.config.initialSize || this.config.width;
39362         if(typeof size != "undefined"){
39363             this.el.setWidth(size);
39364         }
39365     },
39366     
39367     getBox : function(){
39368         if(this.collapsed){
39369             return this.collapsedEl.getBox();
39370         }
39371         var box = this.el.getBox();
39372         if(this.split){
39373             box.width += this.split.el.getWidth();
39374         }
39375         return box;
39376     },
39377     
39378     updateBox : function(box){
39379         if(this.split && !this.collapsed){
39380             var sw = this.split.el.getWidth();
39381             box.width -= sw;
39382             this.split.el.setLeft(box.x+box.width);
39383             this.split.el.setTop(box.y);
39384             this.split.el.setHeight(box.height);
39385         }
39386         if(this.collapsed){
39387             this.updateBody(null, box.height);
39388         }
39389         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39390     }
39391 });Roo.namespace("Roo.bootstrap.panel");/*
39392  * Based on:
39393  * Ext JS Library 1.1.1
39394  * Copyright(c) 2006-2007, Ext JS, LLC.
39395  *
39396  * Originally Released Under LGPL - original licence link has changed is not relivant.
39397  *
39398  * Fork - LGPL
39399  * <script type="text/javascript">
39400  */
39401 /**
39402  * @class Roo.ContentPanel
39403  * @extends Roo.util.Observable
39404  * A basic ContentPanel element.
39405  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39406  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39407  * @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
39408  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39409  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39410  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39411  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39412  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39413  * @cfg {String} title          The title for this panel
39414  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39415  * @cfg {String} url            Calls {@link #setUrl} with this value
39416  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39417  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39418  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39419  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39420  * @cfg {Boolean} badges render the badges
39421  * @cfg {String} cls  extra classes to use  
39422  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39423
39424  * @constructor
39425  * Create a new ContentPanel.
39426  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39427  * @param {String/Object} config A string to set only the title or a config object
39428  * @param {String} content (optional) Set the HTML content for this panel
39429  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39430  */
39431 Roo.bootstrap.panel.Content = function( config){
39432     
39433     this.tpl = config.tpl || false;
39434     
39435     var el = config.el;
39436     var content = config.content;
39437
39438     if(config.autoCreate){ // xtype is available if this is called from factory
39439         el = Roo.id();
39440     }
39441     this.el = Roo.get(el);
39442     if(!this.el && config && config.autoCreate){
39443         if(typeof config.autoCreate == "object"){
39444             if(!config.autoCreate.id){
39445                 config.autoCreate.id = config.id||el;
39446             }
39447             this.el = Roo.DomHelper.append(document.body,
39448                         config.autoCreate, true);
39449         }else{
39450             var elcfg =  {
39451                 tag: "div",
39452                 cls: (config.cls || '') +
39453                     (config.background ? ' bg-' + config.background : '') +
39454                     " roo-layout-inactive-content",
39455                 id: config.id||el
39456             };
39457             if (config.html) {
39458                 elcfg.html = config.html;
39459                 
39460             }
39461                         
39462             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39463         }
39464     } 
39465     this.closable = false;
39466     this.loaded = false;
39467     this.active = false;
39468    
39469       
39470     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39471         
39472         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39473         
39474         this.wrapEl = this.el; //this.el.wrap();
39475         var ti = [];
39476         if (config.toolbar.items) {
39477             ti = config.toolbar.items ;
39478             delete config.toolbar.items ;
39479         }
39480         
39481         var nitems = [];
39482         this.toolbar.render(this.wrapEl, 'before');
39483         for(var i =0;i < ti.length;i++) {
39484           //  Roo.log(['add child', items[i]]);
39485             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39486         }
39487         this.toolbar.items = nitems;
39488         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39489         delete config.toolbar;
39490         
39491     }
39492     /*
39493     // xtype created footer. - not sure if will work as we normally have to render first..
39494     if (this.footer && !this.footer.el && this.footer.xtype) {
39495         if (!this.wrapEl) {
39496             this.wrapEl = this.el.wrap();
39497         }
39498     
39499         this.footer.container = this.wrapEl.createChild();
39500          
39501         this.footer = Roo.factory(this.footer, Roo);
39502         
39503     }
39504     */
39505     
39506      if(typeof config == "string"){
39507         this.title = config;
39508     }else{
39509         Roo.apply(this, config);
39510     }
39511     
39512     if(this.resizeEl){
39513         this.resizeEl = Roo.get(this.resizeEl, true);
39514     }else{
39515         this.resizeEl = this.el;
39516     }
39517     // handle view.xtype
39518     
39519  
39520     
39521     
39522     this.addEvents({
39523         /**
39524          * @event activate
39525          * Fires when this panel is activated. 
39526          * @param {Roo.ContentPanel} this
39527          */
39528         "activate" : true,
39529         /**
39530          * @event deactivate
39531          * Fires when this panel is activated. 
39532          * @param {Roo.ContentPanel} this
39533          */
39534         "deactivate" : true,
39535
39536         /**
39537          * @event resize
39538          * Fires when this panel is resized if fitToFrame is true.
39539          * @param {Roo.ContentPanel} this
39540          * @param {Number} width The width after any component adjustments
39541          * @param {Number} height The height after any component adjustments
39542          */
39543         "resize" : true,
39544         
39545          /**
39546          * @event render
39547          * Fires when this tab is created
39548          * @param {Roo.ContentPanel} this
39549          */
39550         "render" : true
39551         
39552         
39553         
39554     });
39555     
39556
39557     
39558     
39559     if(this.autoScroll){
39560         this.resizeEl.setStyle("overflow", "auto");
39561     } else {
39562         // fix randome scrolling
39563         //this.el.on('scroll', function() {
39564         //    Roo.log('fix random scolling');
39565         //    this.scrollTo('top',0); 
39566         //});
39567     }
39568     content = content || this.content;
39569     if(content){
39570         this.setContent(content);
39571     }
39572     if(config && config.url){
39573         this.setUrl(this.url, this.params, this.loadOnce);
39574     }
39575     
39576     
39577     
39578     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39579     
39580     if (this.view && typeof(this.view.xtype) != 'undefined') {
39581         this.view.el = this.el.appendChild(document.createElement("div"));
39582         this.view = Roo.factory(this.view); 
39583         this.view.render  &&  this.view.render(false, '');  
39584     }
39585     
39586     
39587     this.fireEvent('render', this);
39588 };
39589
39590 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39591     
39592     cls : '',
39593     background : '',
39594     
39595     tabTip : '',
39596     
39597     setRegion : function(region){
39598         this.region = region;
39599         this.setActiveClass(region && !this.background);
39600     },
39601     
39602     
39603     setActiveClass: function(state)
39604     {
39605         if(state){
39606            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39607            this.el.setStyle('position','relative');
39608         }else{
39609            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39610            this.el.setStyle('position', 'absolute');
39611         } 
39612     },
39613     
39614     /**
39615      * Returns the toolbar for this Panel if one was configured. 
39616      * @return {Roo.Toolbar} 
39617      */
39618     getToolbar : function(){
39619         return this.toolbar;
39620     },
39621     
39622     setActiveState : function(active)
39623     {
39624         this.active = active;
39625         this.setActiveClass(active);
39626         if(!active){
39627             if(this.fireEvent("deactivate", this) === false){
39628                 return false;
39629             }
39630             return true;
39631         }
39632         this.fireEvent("activate", this);
39633         return true;
39634     },
39635     /**
39636      * Updates this panel's element
39637      * @param {String} content The new content
39638      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39639     */
39640     setContent : function(content, loadScripts){
39641         this.el.update(content, loadScripts);
39642     },
39643
39644     ignoreResize : function(w, h){
39645         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39646             return true;
39647         }else{
39648             this.lastSize = {width: w, height: h};
39649             return false;
39650         }
39651     },
39652     /**
39653      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39654      * @return {Roo.UpdateManager} The UpdateManager
39655      */
39656     getUpdateManager : function(){
39657         return this.el.getUpdateManager();
39658     },
39659      /**
39660      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39661      * @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:
39662 <pre><code>
39663 panel.load({
39664     url: "your-url.php",
39665     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39666     callback: yourFunction,
39667     scope: yourObject, //(optional scope)
39668     discardUrl: false,
39669     nocache: false,
39670     text: "Loading...",
39671     timeout: 30,
39672     scripts: false
39673 });
39674 </code></pre>
39675      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39676      * 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.
39677      * @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}
39678      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39679      * @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.
39680      * @return {Roo.ContentPanel} this
39681      */
39682     load : function(){
39683         var um = this.el.getUpdateManager();
39684         um.update.apply(um, arguments);
39685         return this;
39686     },
39687
39688
39689     /**
39690      * 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.
39691      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39692      * @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)
39693      * @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)
39694      * @return {Roo.UpdateManager} The UpdateManager
39695      */
39696     setUrl : function(url, params, loadOnce){
39697         if(this.refreshDelegate){
39698             this.removeListener("activate", this.refreshDelegate);
39699         }
39700         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39701         this.on("activate", this.refreshDelegate);
39702         return this.el.getUpdateManager();
39703     },
39704     
39705     _handleRefresh : function(url, params, loadOnce){
39706         if(!loadOnce || !this.loaded){
39707             var updater = this.el.getUpdateManager();
39708             updater.update(url, params, this._setLoaded.createDelegate(this));
39709         }
39710     },
39711     
39712     _setLoaded : function(){
39713         this.loaded = true;
39714     }, 
39715     
39716     /**
39717      * Returns this panel's id
39718      * @return {String} 
39719      */
39720     getId : function(){
39721         return this.el.id;
39722     },
39723     
39724     /** 
39725      * Returns this panel's element - used by regiosn to add.
39726      * @return {Roo.Element} 
39727      */
39728     getEl : function(){
39729         return this.wrapEl || this.el;
39730     },
39731     
39732    
39733     
39734     adjustForComponents : function(width, height)
39735     {
39736         //Roo.log('adjustForComponents ');
39737         if(this.resizeEl != this.el){
39738             width -= this.el.getFrameWidth('lr');
39739             height -= this.el.getFrameWidth('tb');
39740         }
39741         if(this.toolbar){
39742             var te = this.toolbar.getEl();
39743             te.setWidth(width);
39744             height -= te.getHeight();
39745         }
39746         if(this.footer){
39747             var te = this.footer.getEl();
39748             te.setWidth(width);
39749             height -= te.getHeight();
39750         }
39751         
39752         
39753         if(this.adjustments){
39754             width += this.adjustments[0];
39755             height += this.adjustments[1];
39756         }
39757         return {"width": width, "height": height};
39758     },
39759     
39760     setSize : function(width, height){
39761         if(this.fitToFrame && !this.ignoreResize(width, height)){
39762             if(this.fitContainer && this.resizeEl != this.el){
39763                 this.el.setSize(width, height);
39764             }
39765             var size = this.adjustForComponents(width, height);
39766             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39767             this.fireEvent('resize', this, size.width, size.height);
39768         }
39769     },
39770     
39771     /**
39772      * Returns this panel's title
39773      * @return {String} 
39774      */
39775     getTitle : function(){
39776         
39777         if (typeof(this.title) != 'object') {
39778             return this.title;
39779         }
39780         
39781         var t = '';
39782         for (var k in this.title) {
39783             if (!this.title.hasOwnProperty(k)) {
39784                 continue;
39785             }
39786             
39787             if (k.indexOf('-') >= 0) {
39788                 var s = k.split('-');
39789                 for (var i = 0; i<s.length; i++) {
39790                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39791                 }
39792             } else {
39793                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39794             }
39795         }
39796         return t;
39797     },
39798     
39799     /**
39800      * Set this panel's title
39801      * @param {String} title
39802      */
39803     setTitle : function(title){
39804         this.title = title;
39805         if(this.region){
39806             this.region.updatePanelTitle(this, title);
39807         }
39808     },
39809     
39810     /**
39811      * Returns true is this panel was configured to be closable
39812      * @return {Boolean} 
39813      */
39814     isClosable : function(){
39815         return this.closable;
39816     },
39817     
39818     beforeSlide : function(){
39819         this.el.clip();
39820         this.resizeEl.clip();
39821     },
39822     
39823     afterSlide : function(){
39824         this.el.unclip();
39825         this.resizeEl.unclip();
39826     },
39827     
39828     /**
39829      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39830      *   Will fail silently if the {@link #setUrl} method has not been called.
39831      *   This does not activate the panel, just updates its content.
39832      */
39833     refresh : function(){
39834         if(this.refreshDelegate){
39835            this.loaded = false;
39836            this.refreshDelegate();
39837         }
39838     },
39839     
39840     /**
39841      * Destroys this panel
39842      */
39843     destroy : function(){
39844         this.el.removeAllListeners();
39845         var tempEl = document.createElement("span");
39846         tempEl.appendChild(this.el.dom);
39847         tempEl.innerHTML = "";
39848         this.el.remove();
39849         this.el = null;
39850     },
39851     
39852     /**
39853      * form - if the content panel contains a form - this is a reference to it.
39854      * @type {Roo.form.Form}
39855      */
39856     form : false,
39857     /**
39858      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39859      *    This contains a reference to it.
39860      * @type {Roo.View}
39861      */
39862     view : false,
39863     
39864       /**
39865      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39866      * <pre><code>
39867
39868 layout.addxtype({
39869        xtype : 'Form',
39870        items: [ .... ]
39871    }
39872 );
39873
39874 </code></pre>
39875      * @param {Object} cfg Xtype definition of item to add.
39876      */
39877     
39878     
39879     getChildContainer: function () {
39880         return this.getEl();
39881     }
39882     
39883     
39884     /*
39885         var  ret = new Roo.factory(cfg);
39886         return ret;
39887         
39888         
39889         // add form..
39890         if (cfg.xtype.match(/^Form$/)) {
39891             
39892             var el;
39893             //if (this.footer) {
39894             //    el = this.footer.container.insertSibling(false, 'before');
39895             //} else {
39896                 el = this.el.createChild();
39897             //}
39898
39899             this.form = new  Roo.form.Form(cfg);
39900             
39901             
39902             if ( this.form.allItems.length) {
39903                 this.form.render(el.dom);
39904             }
39905             return this.form;
39906         }
39907         // should only have one of theses..
39908         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39909             // views.. should not be just added - used named prop 'view''
39910             
39911             cfg.el = this.el.appendChild(document.createElement("div"));
39912             // factory?
39913             
39914             var ret = new Roo.factory(cfg);
39915              
39916              ret.render && ret.render(false, ''); // render blank..
39917             this.view = ret;
39918             return ret;
39919         }
39920         return false;
39921     }
39922     \*/
39923 });
39924  
39925 /**
39926  * @class Roo.bootstrap.panel.Grid
39927  * @extends Roo.bootstrap.panel.Content
39928  * @constructor
39929  * Create a new GridPanel.
39930  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39931  * @param {Object} config A the config object
39932   
39933  */
39934
39935
39936
39937 Roo.bootstrap.panel.Grid = function(config)
39938 {
39939     
39940       
39941     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39942         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39943
39944     config.el = this.wrapper;
39945     //this.el = this.wrapper;
39946     
39947       if (config.container) {
39948         // ctor'ed from a Border/panel.grid
39949         
39950         
39951         this.wrapper.setStyle("overflow", "hidden");
39952         this.wrapper.addClass('roo-grid-container');
39953
39954     }
39955     
39956     
39957     if(config.toolbar){
39958         var tool_el = this.wrapper.createChild();    
39959         this.toolbar = Roo.factory(config.toolbar);
39960         var ti = [];
39961         if (config.toolbar.items) {
39962             ti = config.toolbar.items ;
39963             delete config.toolbar.items ;
39964         }
39965         
39966         var nitems = [];
39967         this.toolbar.render(tool_el);
39968         for(var i =0;i < ti.length;i++) {
39969           //  Roo.log(['add child', items[i]]);
39970             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39971         }
39972         this.toolbar.items = nitems;
39973         
39974         delete config.toolbar;
39975     }
39976     
39977     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39978     config.grid.scrollBody = true;;
39979     config.grid.monitorWindowResize = false; // turn off autosizing
39980     config.grid.autoHeight = false;
39981     config.grid.autoWidth = false;
39982     
39983     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39984     
39985     if (config.background) {
39986         // render grid on panel activation (if panel background)
39987         this.on('activate', function(gp) {
39988             if (!gp.grid.rendered) {
39989                 gp.grid.render(this.wrapper);
39990                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39991             }
39992         });
39993             
39994     } else {
39995         this.grid.render(this.wrapper);
39996         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39997
39998     }
39999     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40000     // ??? needed ??? config.el = this.wrapper;
40001     
40002     
40003     
40004   
40005     // xtype created footer. - not sure if will work as we normally have to render first..
40006     if (this.footer && !this.footer.el && this.footer.xtype) {
40007         
40008         var ctr = this.grid.getView().getFooterPanel(true);
40009         this.footer.dataSource = this.grid.dataSource;
40010         this.footer = Roo.factory(this.footer, Roo);
40011         this.footer.render(ctr);
40012         
40013     }
40014     
40015     
40016     
40017     
40018      
40019 };
40020
40021 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40022     getId : function(){
40023         return this.grid.id;
40024     },
40025     
40026     /**
40027      * Returns the grid for this panel
40028      * @return {Roo.bootstrap.Table} 
40029      */
40030     getGrid : function(){
40031         return this.grid;    
40032     },
40033     
40034     setSize : function(width, height){
40035         if(!this.ignoreResize(width, height)){
40036             var grid = this.grid;
40037             var size = this.adjustForComponents(width, height);
40038             // tfoot is not a footer?
40039           
40040             
40041             var gridel = grid.getGridEl();
40042             gridel.setSize(size.width, size.height);
40043             
40044             var tbd = grid.getGridEl().select('tbody', true).first();
40045             var thd = grid.getGridEl().select('thead',true).first();
40046             var tbf= grid.getGridEl().select('tfoot', true).first();
40047
40048             if (tbf) {
40049                 size.height -= thd.getHeight();
40050             }
40051             if (thd) {
40052                 size.height -= thd.getHeight();
40053             }
40054             
40055             tbd.setSize(size.width, size.height );
40056             // this is for the account management tab -seems to work there.
40057             var thd = grid.getGridEl().select('thead',true).first();
40058             //if (tbd) {
40059             //    tbd.setSize(size.width, size.height - thd.getHeight());
40060             //}
40061              
40062             grid.autoSize();
40063         }
40064     },
40065      
40066     
40067     
40068     beforeSlide : function(){
40069         this.grid.getView().scroller.clip();
40070     },
40071     
40072     afterSlide : function(){
40073         this.grid.getView().scroller.unclip();
40074     },
40075     
40076     destroy : function(){
40077         this.grid.destroy();
40078         delete this.grid;
40079         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40080     }
40081 });
40082
40083 /**
40084  * @class Roo.bootstrap.panel.Nest
40085  * @extends Roo.bootstrap.panel.Content
40086  * @constructor
40087  * Create a new Panel, that can contain a layout.Border.
40088  * 
40089  * 
40090  * @param {Roo.BorderLayout} layout The layout for this panel
40091  * @param {String/Object} config A string to set only the title or a config object
40092  */
40093 Roo.bootstrap.panel.Nest = function(config)
40094 {
40095     // construct with only one argument..
40096     /* FIXME - implement nicer consturctors
40097     if (layout.layout) {
40098         config = layout;
40099         layout = config.layout;
40100         delete config.layout;
40101     }
40102     if (layout.xtype && !layout.getEl) {
40103         // then layout needs constructing..
40104         layout = Roo.factory(layout, Roo);
40105     }
40106     */
40107     
40108     config.el =  config.layout.getEl();
40109     
40110     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40111     
40112     config.layout.monitorWindowResize = false; // turn off autosizing
40113     this.layout = config.layout;
40114     this.layout.getEl().addClass("roo-layout-nested-layout");
40115     this.layout.parent = this;
40116     
40117     
40118     
40119     
40120 };
40121
40122 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40123
40124     setSize : function(width, height){
40125         if(!this.ignoreResize(width, height)){
40126             var size = this.adjustForComponents(width, height);
40127             var el = this.layout.getEl();
40128             if (size.height < 1) {
40129                 el.setWidth(size.width);   
40130             } else {
40131                 el.setSize(size.width, size.height);
40132             }
40133             var touch = el.dom.offsetWidth;
40134             this.layout.layout();
40135             // ie requires a double layout on the first pass
40136             if(Roo.isIE && !this.initialized){
40137                 this.initialized = true;
40138                 this.layout.layout();
40139             }
40140         }
40141     },
40142     
40143     // activate all subpanels if not currently active..
40144     
40145     setActiveState : function(active){
40146         this.active = active;
40147         this.setActiveClass(active);
40148         
40149         if(!active){
40150             this.fireEvent("deactivate", this);
40151             return;
40152         }
40153         
40154         this.fireEvent("activate", this);
40155         // not sure if this should happen before or after..
40156         if (!this.layout) {
40157             return; // should not happen..
40158         }
40159         var reg = false;
40160         for (var r in this.layout.regions) {
40161             reg = this.layout.getRegion(r);
40162             if (reg.getActivePanel()) {
40163                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40164                 reg.setActivePanel(reg.getActivePanel());
40165                 continue;
40166             }
40167             if (!reg.panels.length) {
40168                 continue;
40169             }
40170             reg.showPanel(reg.getPanel(0));
40171         }
40172         
40173         
40174         
40175         
40176     },
40177     
40178     /**
40179      * Returns the nested BorderLayout for this panel
40180      * @return {Roo.BorderLayout} 
40181      */
40182     getLayout : function(){
40183         return this.layout;
40184     },
40185     
40186      /**
40187      * Adds a xtype elements to the layout of the nested panel
40188      * <pre><code>
40189
40190 panel.addxtype({
40191        xtype : 'ContentPanel',
40192        region: 'west',
40193        items: [ .... ]
40194    }
40195 );
40196
40197 panel.addxtype({
40198         xtype : 'NestedLayoutPanel',
40199         region: 'west',
40200         layout: {
40201            center: { },
40202            west: { }   
40203         },
40204         items : [ ... list of content panels or nested layout panels.. ]
40205    }
40206 );
40207 </code></pre>
40208      * @param {Object} cfg Xtype definition of item to add.
40209      */
40210     addxtype : function(cfg) {
40211         return this.layout.addxtype(cfg);
40212     
40213     }
40214 });/*
40215  * Based on:
40216  * Ext JS Library 1.1.1
40217  * Copyright(c) 2006-2007, Ext JS, LLC.
40218  *
40219  * Originally Released Under LGPL - original licence link has changed is not relivant.
40220  *
40221  * Fork - LGPL
40222  * <script type="text/javascript">
40223  */
40224 /**
40225  * @class Roo.TabPanel
40226  * @extends Roo.util.Observable
40227  * A lightweight tab container.
40228  * <br><br>
40229  * Usage:
40230  * <pre><code>
40231 // basic tabs 1, built from existing content
40232 var tabs = new Roo.TabPanel("tabs1");
40233 tabs.addTab("script", "View Script");
40234 tabs.addTab("markup", "View Markup");
40235 tabs.activate("script");
40236
40237 // more advanced tabs, built from javascript
40238 var jtabs = new Roo.TabPanel("jtabs");
40239 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40240
40241 // set up the UpdateManager
40242 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40243 var updater = tab2.getUpdateManager();
40244 updater.setDefaultUrl("ajax1.htm");
40245 tab2.on('activate', updater.refresh, updater, true);
40246
40247 // Use setUrl for Ajax loading
40248 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40249 tab3.setUrl("ajax2.htm", null, true);
40250
40251 // Disabled tab
40252 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40253 tab4.disable();
40254
40255 jtabs.activate("jtabs-1");
40256  * </code></pre>
40257  * @constructor
40258  * Create a new TabPanel.
40259  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40260  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40261  */
40262 Roo.bootstrap.panel.Tabs = function(config){
40263     /**
40264     * The container element for this TabPanel.
40265     * @type Roo.Element
40266     */
40267     this.el = Roo.get(config.el);
40268     delete config.el;
40269     if(config){
40270         if(typeof config == "boolean"){
40271             this.tabPosition = config ? "bottom" : "top";
40272         }else{
40273             Roo.apply(this, config);
40274         }
40275     }
40276     
40277     if(this.tabPosition == "bottom"){
40278         // if tabs are at the bottom = create the body first.
40279         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40280         this.el.addClass("roo-tabs-bottom");
40281     }
40282     // next create the tabs holders
40283     
40284     if (this.tabPosition == "west"){
40285         
40286         var reg = this.region; // fake it..
40287         while (reg) {
40288             if (!reg.mgr.parent) {
40289                 break;
40290             }
40291             reg = reg.mgr.parent.region;
40292         }
40293         Roo.log("got nest?");
40294         Roo.log(reg);
40295         if (reg.mgr.getRegion('west')) {
40296             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40297             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40298             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40299             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40300             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40301         
40302             
40303         }
40304         
40305         
40306     } else {
40307      
40308         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40309         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40310         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40311         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40312     }
40313     
40314     
40315     if(Roo.isIE){
40316         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40317     }
40318     
40319     // finally - if tabs are at the top, then create the body last..
40320     if(this.tabPosition != "bottom"){
40321         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40322          * @type Roo.Element
40323          */
40324         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40325         this.el.addClass("roo-tabs-top");
40326     }
40327     this.items = [];
40328
40329     this.bodyEl.setStyle("position", "relative");
40330
40331     this.active = null;
40332     this.activateDelegate = this.activate.createDelegate(this);
40333
40334     this.addEvents({
40335         /**
40336          * @event tabchange
40337          * Fires when the active tab changes
40338          * @param {Roo.TabPanel} this
40339          * @param {Roo.TabPanelItem} activePanel The new active tab
40340          */
40341         "tabchange": true,
40342         /**
40343          * @event beforetabchange
40344          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40345          * @param {Roo.TabPanel} this
40346          * @param {Object} e Set cancel to true on this object to cancel the tab change
40347          * @param {Roo.TabPanelItem} tab The tab being changed to
40348          */
40349         "beforetabchange" : true
40350     });
40351
40352     Roo.EventManager.onWindowResize(this.onResize, this);
40353     this.cpad = this.el.getPadding("lr");
40354     this.hiddenCount = 0;
40355
40356
40357     // toolbar on the tabbar support...
40358     if (this.toolbar) {
40359         alert("no toolbar support yet");
40360         this.toolbar  = false;
40361         /*
40362         var tcfg = this.toolbar;
40363         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40364         this.toolbar = new Roo.Toolbar(tcfg);
40365         if (Roo.isSafari) {
40366             var tbl = tcfg.container.child('table', true);
40367             tbl.setAttribute('width', '100%');
40368         }
40369         */
40370         
40371     }
40372    
40373
40374
40375     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40376 };
40377
40378 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40379     /*
40380      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40381      */
40382     tabPosition : "top",
40383     /*
40384      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40385      */
40386     currentTabWidth : 0,
40387     /*
40388      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40389      */
40390     minTabWidth : 40,
40391     /*
40392      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40393      */
40394     maxTabWidth : 250,
40395     /*
40396      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40397      */
40398     preferredTabWidth : 175,
40399     /*
40400      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40401      */
40402     resizeTabs : false,
40403     /*
40404      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40405      */
40406     monitorResize : true,
40407     /*
40408      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40409      */
40410     toolbar : false,  // set by caller..
40411     
40412     region : false, /// set by caller
40413     
40414     disableTooltips : true, // not used yet...
40415
40416     /**
40417      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40418      * @param {String} id The id of the div to use <b>or create</b>
40419      * @param {String} text The text for the tab
40420      * @param {String} content (optional) Content to put in the TabPanelItem body
40421      * @param {Boolean} closable (optional) True to create a close icon on the tab
40422      * @return {Roo.TabPanelItem} The created TabPanelItem
40423      */
40424     addTab : function(id, text, content, closable, tpl)
40425     {
40426         var item = new Roo.bootstrap.panel.TabItem({
40427             panel: this,
40428             id : id,
40429             text : text,
40430             closable : closable,
40431             tpl : tpl
40432         });
40433         this.addTabItem(item);
40434         if(content){
40435             item.setContent(content);
40436         }
40437         return item;
40438     },
40439
40440     /**
40441      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40442      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40443      * @return {Roo.TabPanelItem}
40444      */
40445     getTab : function(id){
40446         return this.items[id];
40447     },
40448
40449     /**
40450      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40451      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40452      */
40453     hideTab : function(id){
40454         var t = this.items[id];
40455         if(!t.isHidden()){
40456            t.setHidden(true);
40457            this.hiddenCount++;
40458            this.autoSizeTabs();
40459         }
40460     },
40461
40462     /**
40463      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40464      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40465      */
40466     unhideTab : function(id){
40467         var t = this.items[id];
40468         if(t.isHidden()){
40469            t.setHidden(false);
40470            this.hiddenCount--;
40471            this.autoSizeTabs();
40472         }
40473     },
40474
40475     /**
40476      * Adds an existing {@link Roo.TabPanelItem}.
40477      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40478      */
40479     addTabItem : function(item)
40480     {
40481         this.items[item.id] = item;
40482         this.items.push(item);
40483         this.autoSizeTabs();
40484       //  if(this.resizeTabs){
40485     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40486   //         this.autoSizeTabs();
40487 //        }else{
40488 //            item.autoSize();
40489        // }
40490     },
40491
40492     /**
40493      * Removes a {@link Roo.TabPanelItem}.
40494      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40495      */
40496     removeTab : function(id){
40497         var items = this.items;
40498         var tab = items[id];
40499         if(!tab) { return; }
40500         var index = items.indexOf(tab);
40501         if(this.active == tab && items.length > 1){
40502             var newTab = this.getNextAvailable(index);
40503             if(newTab) {
40504                 newTab.activate();
40505             }
40506         }
40507         this.stripEl.dom.removeChild(tab.pnode.dom);
40508         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40509             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40510         }
40511         items.splice(index, 1);
40512         delete this.items[tab.id];
40513         tab.fireEvent("close", tab);
40514         tab.purgeListeners();
40515         this.autoSizeTabs();
40516     },
40517
40518     getNextAvailable : function(start){
40519         var items = this.items;
40520         var index = start;
40521         // look for a next tab that will slide over to
40522         // replace the one being removed
40523         while(index < items.length){
40524             var item = items[++index];
40525             if(item && !item.isHidden()){
40526                 return item;
40527             }
40528         }
40529         // if one isn't found select the previous tab (on the left)
40530         index = start;
40531         while(index >= 0){
40532             var item = items[--index];
40533             if(item && !item.isHidden()){
40534                 return item;
40535             }
40536         }
40537         return null;
40538     },
40539
40540     /**
40541      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40542      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40543      */
40544     disableTab : function(id){
40545         var tab = this.items[id];
40546         if(tab && this.active != tab){
40547             tab.disable();
40548         }
40549     },
40550
40551     /**
40552      * Enables a {@link Roo.TabPanelItem} that is disabled.
40553      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40554      */
40555     enableTab : function(id){
40556         var tab = this.items[id];
40557         tab.enable();
40558     },
40559
40560     /**
40561      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40562      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40563      * @return {Roo.TabPanelItem} The TabPanelItem.
40564      */
40565     activate : function(id)
40566     {
40567         //Roo.log('activite:'  + id);
40568         
40569         var tab = this.items[id];
40570         if(!tab){
40571             return null;
40572         }
40573         if(tab == this.active || tab.disabled){
40574             return tab;
40575         }
40576         var e = {};
40577         this.fireEvent("beforetabchange", this, e, tab);
40578         if(e.cancel !== true && !tab.disabled){
40579             if(this.active){
40580                 this.active.hide();
40581             }
40582             this.active = this.items[id];
40583             this.active.show();
40584             this.fireEvent("tabchange", this, this.active);
40585         }
40586         return tab;
40587     },
40588
40589     /**
40590      * Gets the active {@link Roo.TabPanelItem}.
40591      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40592      */
40593     getActiveTab : function(){
40594         return this.active;
40595     },
40596
40597     /**
40598      * Updates the tab body element to fit the height of the container element
40599      * for overflow scrolling
40600      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40601      */
40602     syncHeight : function(targetHeight){
40603         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40604         var bm = this.bodyEl.getMargins();
40605         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40606         this.bodyEl.setHeight(newHeight);
40607         return newHeight;
40608     },
40609
40610     onResize : function(){
40611         if(this.monitorResize){
40612             this.autoSizeTabs();
40613         }
40614     },
40615
40616     /**
40617      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40618      */
40619     beginUpdate : function(){
40620         this.updating = true;
40621     },
40622
40623     /**
40624      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40625      */
40626     endUpdate : function(){
40627         this.updating = false;
40628         this.autoSizeTabs();
40629     },
40630
40631     /**
40632      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40633      */
40634     autoSizeTabs : function()
40635     {
40636         var count = this.items.length;
40637         var vcount = count - this.hiddenCount;
40638         
40639         if (vcount < 2) {
40640             this.stripEl.hide();
40641         } else {
40642             this.stripEl.show();
40643         }
40644         
40645         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40646             return;
40647         }
40648         
40649         
40650         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40651         var availWidth = Math.floor(w / vcount);
40652         var b = this.stripBody;
40653         if(b.getWidth() > w){
40654             var tabs = this.items;
40655             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40656             if(availWidth < this.minTabWidth){
40657                 /*if(!this.sleft){    // incomplete scrolling code
40658                     this.createScrollButtons();
40659                 }
40660                 this.showScroll();
40661                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40662             }
40663         }else{
40664             if(this.currentTabWidth < this.preferredTabWidth){
40665                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40666             }
40667         }
40668     },
40669
40670     /**
40671      * Returns the number of tabs in this TabPanel.
40672      * @return {Number}
40673      */
40674      getCount : function(){
40675          return this.items.length;
40676      },
40677
40678     /**
40679      * Resizes all the tabs to the passed width
40680      * @param {Number} The new width
40681      */
40682     setTabWidth : function(width){
40683         this.currentTabWidth = width;
40684         for(var i = 0, len = this.items.length; i < len; i++) {
40685                 if(!this.items[i].isHidden()) {
40686                 this.items[i].setWidth(width);
40687             }
40688         }
40689     },
40690
40691     /**
40692      * Destroys this TabPanel
40693      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40694      */
40695     destroy : function(removeEl){
40696         Roo.EventManager.removeResizeListener(this.onResize, this);
40697         for(var i = 0, len = this.items.length; i < len; i++){
40698             this.items[i].purgeListeners();
40699         }
40700         if(removeEl === true){
40701             this.el.update("");
40702             this.el.remove();
40703         }
40704     },
40705     
40706     createStrip : function(container)
40707     {
40708         var strip = document.createElement("nav");
40709         strip.className = Roo.bootstrap.version == 4 ?
40710             "navbar-light bg-light" : 
40711             "navbar navbar-default"; //"x-tabs-wrap";
40712         container.appendChild(strip);
40713         return strip;
40714     },
40715     
40716     createStripList : function(strip)
40717     {
40718         // div wrapper for retard IE
40719         // returns the "tr" element.
40720         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40721         //'<div class="x-tabs-strip-wrap">'+
40722           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40723           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40724         return strip.firstChild; //.firstChild.firstChild.firstChild;
40725     },
40726     createBody : function(container)
40727     {
40728         var body = document.createElement("div");
40729         Roo.id(body, "tab-body");
40730         //Roo.fly(body).addClass("x-tabs-body");
40731         Roo.fly(body).addClass("tab-content");
40732         container.appendChild(body);
40733         return body;
40734     },
40735     createItemBody :function(bodyEl, id){
40736         var body = Roo.getDom(id);
40737         if(!body){
40738             body = document.createElement("div");
40739             body.id = id;
40740         }
40741         //Roo.fly(body).addClass("x-tabs-item-body");
40742         Roo.fly(body).addClass("tab-pane");
40743          bodyEl.insertBefore(body, bodyEl.firstChild);
40744         return body;
40745     },
40746     /** @private */
40747     createStripElements :  function(stripEl, text, closable, tpl)
40748     {
40749         var td = document.createElement("li"); // was td..
40750         td.className = 'nav-item';
40751         
40752         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40753         
40754         
40755         stripEl.appendChild(td);
40756         /*if(closable){
40757             td.className = "x-tabs-closable";
40758             if(!this.closeTpl){
40759                 this.closeTpl = new Roo.Template(
40760                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40761                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40762                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40763                 );
40764             }
40765             var el = this.closeTpl.overwrite(td, {"text": text});
40766             var close = el.getElementsByTagName("div")[0];
40767             var inner = el.getElementsByTagName("em")[0];
40768             return {"el": el, "close": close, "inner": inner};
40769         } else {
40770         */
40771         // not sure what this is..
40772 //            if(!this.tabTpl){
40773                 //this.tabTpl = new Roo.Template(
40774                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40775                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40776                 //);
40777 //                this.tabTpl = new Roo.Template(
40778 //                   '<a href="#">' +
40779 //                   '<span unselectable="on"' +
40780 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40781 //                            ' >{text}</span></a>'
40782 //                );
40783 //                
40784 //            }
40785
40786
40787             var template = tpl || this.tabTpl || false;
40788             
40789             if(!template){
40790                 template =  new Roo.Template(
40791                         Roo.bootstrap.version == 4 ? 
40792                             (
40793                                 '<a class="nav-link" href="#" unselectable="on"' +
40794                                      (this.disableTooltips ? '' : ' title="{text}"') +
40795                                      ' >{text}</a>'
40796                             ) : (
40797                                 '<a class="nav-link" href="#">' +
40798                                 '<span unselectable="on"' +
40799                                          (this.disableTooltips ? '' : ' title="{text}"') +
40800                                     ' >{text}</span></a>'
40801                             )
40802                 );
40803             }
40804             
40805             switch (typeof(template)) {
40806                 case 'object' :
40807                     break;
40808                 case 'string' :
40809                     template = new Roo.Template(template);
40810                     break;
40811                 default :
40812                     break;
40813             }
40814             
40815             var el = template.overwrite(td, {"text": text});
40816             
40817             var inner = el.getElementsByTagName("span")[0];
40818             
40819             return {"el": el, "inner": inner};
40820             
40821     }
40822         
40823     
40824 });
40825
40826 /**
40827  * @class Roo.TabPanelItem
40828  * @extends Roo.util.Observable
40829  * Represents an individual item (tab plus body) in a TabPanel.
40830  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40831  * @param {String} id The id of this TabPanelItem
40832  * @param {String} text The text for the tab of this TabPanelItem
40833  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40834  */
40835 Roo.bootstrap.panel.TabItem = function(config){
40836     /**
40837      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40838      * @type Roo.TabPanel
40839      */
40840     this.tabPanel = config.panel;
40841     /**
40842      * The id for this TabPanelItem
40843      * @type String
40844      */
40845     this.id = config.id;
40846     /** @private */
40847     this.disabled = false;
40848     /** @private */
40849     this.text = config.text;
40850     /** @private */
40851     this.loaded = false;
40852     this.closable = config.closable;
40853
40854     /**
40855      * The body element for this TabPanelItem.
40856      * @type Roo.Element
40857      */
40858     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40859     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40860     this.bodyEl.setStyle("display", "block");
40861     this.bodyEl.setStyle("zoom", "1");
40862     //this.hideAction();
40863
40864     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40865     /** @private */
40866     this.el = Roo.get(els.el);
40867     this.inner = Roo.get(els.inner, true);
40868      this.textEl = Roo.bootstrap.version == 4 ?
40869         this.el : Roo.get(this.el.dom.firstChild, true);
40870
40871     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40872     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40873
40874     
40875 //    this.el.on("mousedown", this.onTabMouseDown, this);
40876     this.el.on("click", this.onTabClick, this);
40877     /** @private */
40878     if(config.closable){
40879         var c = Roo.get(els.close, true);
40880         c.dom.title = this.closeText;
40881         c.addClassOnOver("close-over");
40882         c.on("click", this.closeClick, this);
40883      }
40884
40885     this.addEvents({
40886          /**
40887          * @event activate
40888          * Fires when this tab becomes the active tab.
40889          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40890          * @param {Roo.TabPanelItem} this
40891          */
40892         "activate": true,
40893         /**
40894          * @event beforeclose
40895          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40896          * @param {Roo.TabPanelItem} this
40897          * @param {Object} e Set cancel to true on this object to cancel the close.
40898          */
40899         "beforeclose": true,
40900         /**
40901          * @event close
40902          * Fires when this tab is closed.
40903          * @param {Roo.TabPanelItem} this
40904          */
40905          "close": true,
40906         /**
40907          * @event deactivate
40908          * Fires when this tab is no longer the active tab.
40909          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40910          * @param {Roo.TabPanelItem} this
40911          */
40912          "deactivate" : true
40913     });
40914     this.hidden = false;
40915
40916     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40917 };
40918
40919 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40920            {
40921     purgeListeners : function(){
40922        Roo.util.Observable.prototype.purgeListeners.call(this);
40923        this.el.removeAllListeners();
40924     },
40925     /**
40926      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40927      */
40928     show : function(){
40929         this.status_node.addClass("active");
40930         this.showAction();
40931         if(Roo.isOpera){
40932             this.tabPanel.stripWrap.repaint();
40933         }
40934         this.fireEvent("activate", this.tabPanel, this);
40935     },
40936
40937     /**
40938      * Returns true if this tab is the active tab.
40939      * @return {Boolean}
40940      */
40941     isActive : function(){
40942         return this.tabPanel.getActiveTab() == this;
40943     },
40944
40945     /**
40946      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40947      */
40948     hide : function(){
40949         this.status_node.removeClass("active");
40950         this.hideAction();
40951         this.fireEvent("deactivate", this.tabPanel, this);
40952     },
40953
40954     hideAction : function(){
40955         this.bodyEl.hide();
40956         this.bodyEl.setStyle("position", "absolute");
40957         this.bodyEl.setLeft("-20000px");
40958         this.bodyEl.setTop("-20000px");
40959     },
40960
40961     showAction : function(){
40962         this.bodyEl.setStyle("position", "relative");
40963         this.bodyEl.setTop("");
40964         this.bodyEl.setLeft("");
40965         this.bodyEl.show();
40966     },
40967
40968     /**
40969      * Set the tooltip for the tab.
40970      * @param {String} tooltip The tab's tooltip
40971      */
40972     setTooltip : function(text){
40973         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40974             this.textEl.dom.qtip = text;
40975             this.textEl.dom.removeAttribute('title');
40976         }else{
40977             this.textEl.dom.title = text;
40978         }
40979     },
40980
40981     onTabClick : function(e){
40982         e.preventDefault();
40983         this.tabPanel.activate(this.id);
40984     },
40985
40986     onTabMouseDown : function(e){
40987         e.preventDefault();
40988         this.tabPanel.activate(this.id);
40989     },
40990 /*
40991     getWidth : function(){
40992         return this.inner.getWidth();
40993     },
40994
40995     setWidth : function(width){
40996         var iwidth = width - this.linode.getPadding("lr");
40997         this.inner.setWidth(iwidth);
40998         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40999         this.linode.setWidth(width);
41000     },
41001 */
41002     /**
41003      * Show or hide the tab
41004      * @param {Boolean} hidden True to hide or false to show.
41005      */
41006     setHidden : function(hidden){
41007         this.hidden = hidden;
41008         this.linode.setStyle("display", hidden ? "none" : "");
41009     },
41010
41011     /**
41012      * Returns true if this tab is "hidden"
41013      * @return {Boolean}
41014      */
41015     isHidden : function(){
41016         return this.hidden;
41017     },
41018
41019     /**
41020      * Returns the text for this tab
41021      * @return {String}
41022      */
41023     getText : function(){
41024         return this.text;
41025     },
41026     /*
41027     autoSize : function(){
41028         //this.el.beginMeasure();
41029         this.textEl.setWidth(1);
41030         /*
41031          *  #2804 [new] Tabs in Roojs
41032          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41033          */
41034         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41035         //this.el.endMeasure();
41036     //},
41037
41038     /**
41039      * Sets the text for the tab (Note: this also sets the tooltip text)
41040      * @param {String} text The tab's text and tooltip
41041      */
41042     setText : function(text){
41043         this.text = text;
41044         this.textEl.update(text);
41045         this.setTooltip(text);
41046         //if(!this.tabPanel.resizeTabs){
41047         //    this.autoSize();
41048         //}
41049     },
41050     /**
41051      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41052      */
41053     activate : function(){
41054         this.tabPanel.activate(this.id);
41055     },
41056
41057     /**
41058      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41059      */
41060     disable : function(){
41061         if(this.tabPanel.active != this){
41062             this.disabled = true;
41063             this.status_node.addClass("disabled");
41064         }
41065     },
41066
41067     /**
41068      * Enables this TabPanelItem if it was previously disabled.
41069      */
41070     enable : function(){
41071         this.disabled = false;
41072         this.status_node.removeClass("disabled");
41073     },
41074
41075     /**
41076      * Sets the content for this TabPanelItem.
41077      * @param {String} content The content
41078      * @param {Boolean} loadScripts true to look for and load scripts
41079      */
41080     setContent : function(content, loadScripts){
41081         this.bodyEl.update(content, loadScripts);
41082     },
41083
41084     /**
41085      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41086      * @return {Roo.UpdateManager} The UpdateManager
41087      */
41088     getUpdateManager : function(){
41089         return this.bodyEl.getUpdateManager();
41090     },
41091
41092     /**
41093      * Set a URL to be used to load the content for this TabPanelItem.
41094      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41095      * @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)
41096      * @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)
41097      * @return {Roo.UpdateManager} The UpdateManager
41098      */
41099     setUrl : function(url, params, loadOnce){
41100         if(this.refreshDelegate){
41101             this.un('activate', this.refreshDelegate);
41102         }
41103         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41104         this.on("activate", this.refreshDelegate);
41105         return this.bodyEl.getUpdateManager();
41106     },
41107
41108     /** @private */
41109     _handleRefresh : function(url, params, loadOnce){
41110         if(!loadOnce || !this.loaded){
41111             var updater = this.bodyEl.getUpdateManager();
41112             updater.update(url, params, this._setLoaded.createDelegate(this));
41113         }
41114     },
41115
41116     /**
41117      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41118      *   Will fail silently if the setUrl method has not been called.
41119      *   This does not activate the panel, just updates its content.
41120      */
41121     refresh : function(){
41122         if(this.refreshDelegate){
41123            this.loaded = false;
41124            this.refreshDelegate();
41125         }
41126     },
41127
41128     /** @private */
41129     _setLoaded : function(){
41130         this.loaded = true;
41131     },
41132
41133     /** @private */
41134     closeClick : function(e){
41135         var o = {};
41136         e.stopEvent();
41137         this.fireEvent("beforeclose", this, o);
41138         if(o.cancel !== true){
41139             this.tabPanel.removeTab(this.id);
41140         }
41141     },
41142     /**
41143      * The text displayed in the tooltip for the close icon.
41144      * @type String
41145      */
41146     closeText : "Close this tab"
41147 });
41148 /**
41149 *    This script refer to:
41150 *    Title: International Telephone Input
41151 *    Author: Jack O'Connor
41152 *    Code version:  v12.1.12
41153 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41154 **/
41155
41156 Roo.bootstrap.PhoneInputData = function() {
41157     var d = [
41158       [
41159         "Afghanistan (‫افغانستان‬‎)",
41160         "af",
41161         "93"
41162       ],
41163       [
41164         "Albania (Shqipëri)",
41165         "al",
41166         "355"
41167       ],
41168       [
41169         "Algeria (‫الجزائر‬‎)",
41170         "dz",
41171         "213"
41172       ],
41173       [
41174         "American Samoa",
41175         "as",
41176         "1684"
41177       ],
41178       [
41179         "Andorra",
41180         "ad",
41181         "376"
41182       ],
41183       [
41184         "Angola",
41185         "ao",
41186         "244"
41187       ],
41188       [
41189         "Anguilla",
41190         "ai",
41191         "1264"
41192       ],
41193       [
41194         "Antigua and Barbuda",
41195         "ag",
41196         "1268"
41197       ],
41198       [
41199         "Argentina",
41200         "ar",
41201         "54"
41202       ],
41203       [
41204         "Armenia (Հայաստան)",
41205         "am",
41206         "374"
41207       ],
41208       [
41209         "Aruba",
41210         "aw",
41211         "297"
41212       ],
41213       [
41214         "Australia",
41215         "au",
41216         "61",
41217         0
41218       ],
41219       [
41220         "Austria (Österreich)",
41221         "at",
41222         "43"
41223       ],
41224       [
41225         "Azerbaijan (Azərbaycan)",
41226         "az",
41227         "994"
41228       ],
41229       [
41230         "Bahamas",
41231         "bs",
41232         "1242"
41233       ],
41234       [
41235         "Bahrain (‫البحرين‬‎)",
41236         "bh",
41237         "973"
41238       ],
41239       [
41240         "Bangladesh (বাংলাদেশ)",
41241         "bd",
41242         "880"
41243       ],
41244       [
41245         "Barbados",
41246         "bb",
41247         "1246"
41248       ],
41249       [
41250         "Belarus (Беларусь)",
41251         "by",
41252         "375"
41253       ],
41254       [
41255         "Belgium (België)",
41256         "be",
41257         "32"
41258       ],
41259       [
41260         "Belize",
41261         "bz",
41262         "501"
41263       ],
41264       [
41265         "Benin (Bénin)",
41266         "bj",
41267         "229"
41268       ],
41269       [
41270         "Bermuda",
41271         "bm",
41272         "1441"
41273       ],
41274       [
41275         "Bhutan (འབྲུག)",
41276         "bt",
41277         "975"
41278       ],
41279       [
41280         "Bolivia",
41281         "bo",
41282         "591"
41283       ],
41284       [
41285         "Bosnia and Herzegovina (Босна и Херцеговина)",
41286         "ba",
41287         "387"
41288       ],
41289       [
41290         "Botswana",
41291         "bw",
41292         "267"
41293       ],
41294       [
41295         "Brazil (Brasil)",
41296         "br",
41297         "55"
41298       ],
41299       [
41300         "British Indian Ocean Territory",
41301         "io",
41302         "246"
41303       ],
41304       [
41305         "British Virgin Islands",
41306         "vg",
41307         "1284"
41308       ],
41309       [
41310         "Brunei",
41311         "bn",
41312         "673"
41313       ],
41314       [
41315         "Bulgaria (България)",
41316         "bg",
41317         "359"
41318       ],
41319       [
41320         "Burkina Faso",
41321         "bf",
41322         "226"
41323       ],
41324       [
41325         "Burundi (Uburundi)",
41326         "bi",
41327         "257"
41328       ],
41329       [
41330         "Cambodia (កម្ពុជា)",
41331         "kh",
41332         "855"
41333       ],
41334       [
41335         "Cameroon (Cameroun)",
41336         "cm",
41337         "237"
41338       ],
41339       [
41340         "Canada",
41341         "ca",
41342         "1",
41343         1,
41344         ["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"]
41345       ],
41346       [
41347         "Cape Verde (Kabu Verdi)",
41348         "cv",
41349         "238"
41350       ],
41351       [
41352         "Caribbean Netherlands",
41353         "bq",
41354         "599",
41355         1
41356       ],
41357       [
41358         "Cayman Islands",
41359         "ky",
41360         "1345"
41361       ],
41362       [
41363         "Central African Republic (République centrafricaine)",
41364         "cf",
41365         "236"
41366       ],
41367       [
41368         "Chad (Tchad)",
41369         "td",
41370         "235"
41371       ],
41372       [
41373         "Chile",
41374         "cl",
41375         "56"
41376       ],
41377       [
41378         "China (中国)",
41379         "cn",
41380         "86"
41381       ],
41382       [
41383         "Christmas Island",
41384         "cx",
41385         "61",
41386         2
41387       ],
41388       [
41389         "Cocos (Keeling) Islands",
41390         "cc",
41391         "61",
41392         1
41393       ],
41394       [
41395         "Colombia",
41396         "co",
41397         "57"
41398       ],
41399       [
41400         "Comoros (‫جزر القمر‬‎)",
41401         "km",
41402         "269"
41403       ],
41404       [
41405         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41406         "cd",
41407         "243"
41408       ],
41409       [
41410         "Congo (Republic) (Congo-Brazzaville)",
41411         "cg",
41412         "242"
41413       ],
41414       [
41415         "Cook Islands",
41416         "ck",
41417         "682"
41418       ],
41419       [
41420         "Costa Rica",
41421         "cr",
41422         "506"
41423       ],
41424       [
41425         "Côte d’Ivoire",
41426         "ci",
41427         "225"
41428       ],
41429       [
41430         "Croatia (Hrvatska)",
41431         "hr",
41432         "385"
41433       ],
41434       [
41435         "Cuba",
41436         "cu",
41437         "53"
41438       ],
41439       [
41440         "Curaçao",
41441         "cw",
41442         "599",
41443         0
41444       ],
41445       [
41446         "Cyprus (Κύπρος)",
41447         "cy",
41448         "357"
41449       ],
41450       [
41451         "Czech Republic (Česká republika)",
41452         "cz",
41453         "420"
41454       ],
41455       [
41456         "Denmark (Danmark)",
41457         "dk",
41458         "45"
41459       ],
41460       [
41461         "Djibouti",
41462         "dj",
41463         "253"
41464       ],
41465       [
41466         "Dominica",
41467         "dm",
41468         "1767"
41469       ],
41470       [
41471         "Dominican Republic (República Dominicana)",
41472         "do",
41473         "1",
41474         2,
41475         ["809", "829", "849"]
41476       ],
41477       [
41478         "Ecuador",
41479         "ec",
41480         "593"
41481       ],
41482       [
41483         "Egypt (‫مصر‬‎)",
41484         "eg",
41485         "20"
41486       ],
41487       [
41488         "El Salvador",
41489         "sv",
41490         "503"
41491       ],
41492       [
41493         "Equatorial Guinea (Guinea Ecuatorial)",
41494         "gq",
41495         "240"
41496       ],
41497       [
41498         "Eritrea",
41499         "er",
41500         "291"
41501       ],
41502       [
41503         "Estonia (Eesti)",
41504         "ee",
41505         "372"
41506       ],
41507       [
41508         "Ethiopia",
41509         "et",
41510         "251"
41511       ],
41512       [
41513         "Falkland Islands (Islas Malvinas)",
41514         "fk",
41515         "500"
41516       ],
41517       [
41518         "Faroe Islands (Føroyar)",
41519         "fo",
41520         "298"
41521       ],
41522       [
41523         "Fiji",
41524         "fj",
41525         "679"
41526       ],
41527       [
41528         "Finland (Suomi)",
41529         "fi",
41530         "358",
41531         0
41532       ],
41533       [
41534         "France",
41535         "fr",
41536         "33"
41537       ],
41538       [
41539         "French Guiana (Guyane française)",
41540         "gf",
41541         "594"
41542       ],
41543       [
41544         "French Polynesia (Polynésie française)",
41545         "pf",
41546         "689"
41547       ],
41548       [
41549         "Gabon",
41550         "ga",
41551         "241"
41552       ],
41553       [
41554         "Gambia",
41555         "gm",
41556         "220"
41557       ],
41558       [
41559         "Georgia (საქართველო)",
41560         "ge",
41561         "995"
41562       ],
41563       [
41564         "Germany (Deutschland)",
41565         "de",
41566         "49"
41567       ],
41568       [
41569         "Ghana (Gaana)",
41570         "gh",
41571         "233"
41572       ],
41573       [
41574         "Gibraltar",
41575         "gi",
41576         "350"
41577       ],
41578       [
41579         "Greece (Ελλάδα)",
41580         "gr",
41581         "30"
41582       ],
41583       [
41584         "Greenland (Kalaallit Nunaat)",
41585         "gl",
41586         "299"
41587       ],
41588       [
41589         "Grenada",
41590         "gd",
41591         "1473"
41592       ],
41593       [
41594         "Guadeloupe",
41595         "gp",
41596         "590",
41597         0
41598       ],
41599       [
41600         "Guam",
41601         "gu",
41602         "1671"
41603       ],
41604       [
41605         "Guatemala",
41606         "gt",
41607         "502"
41608       ],
41609       [
41610         "Guernsey",
41611         "gg",
41612         "44",
41613         1
41614       ],
41615       [
41616         "Guinea (Guinée)",
41617         "gn",
41618         "224"
41619       ],
41620       [
41621         "Guinea-Bissau (Guiné Bissau)",
41622         "gw",
41623         "245"
41624       ],
41625       [
41626         "Guyana",
41627         "gy",
41628         "592"
41629       ],
41630       [
41631         "Haiti",
41632         "ht",
41633         "509"
41634       ],
41635       [
41636         "Honduras",
41637         "hn",
41638         "504"
41639       ],
41640       [
41641         "Hong Kong (香港)",
41642         "hk",
41643         "852"
41644       ],
41645       [
41646         "Hungary (Magyarország)",
41647         "hu",
41648         "36"
41649       ],
41650       [
41651         "Iceland (Ísland)",
41652         "is",
41653         "354"
41654       ],
41655       [
41656         "India (भारत)",
41657         "in",
41658         "91"
41659       ],
41660       [
41661         "Indonesia",
41662         "id",
41663         "62"
41664       ],
41665       [
41666         "Iran (‫ایران‬‎)",
41667         "ir",
41668         "98"
41669       ],
41670       [
41671         "Iraq (‫العراق‬‎)",
41672         "iq",
41673         "964"
41674       ],
41675       [
41676         "Ireland",
41677         "ie",
41678         "353"
41679       ],
41680       [
41681         "Isle of Man",
41682         "im",
41683         "44",
41684         2
41685       ],
41686       [
41687         "Israel (‫ישראל‬‎)",
41688         "il",
41689         "972"
41690       ],
41691       [
41692         "Italy (Italia)",
41693         "it",
41694         "39",
41695         0
41696       ],
41697       [
41698         "Jamaica",
41699         "jm",
41700         "1876"
41701       ],
41702       [
41703         "Japan (日本)",
41704         "jp",
41705         "81"
41706       ],
41707       [
41708         "Jersey",
41709         "je",
41710         "44",
41711         3
41712       ],
41713       [
41714         "Jordan (‫الأردن‬‎)",
41715         "jo",
41716         "962"
41717       ],
41718       [
41719         "Kazakhstan (Казахстан)",
41720         "kz",
41721         "7",
41722         1
41723       ],
41724       [
41725         "Kenya",
41726         "ke",
41727         "254"
41728       ],
41729       [
41730         "Kiribati",
41731         "ki",
41732         "686"
41733       ],
41734       [
41735         "Kosovo",
41736         "xk",
41737         "383"
41738       ],
41739       [
41740         "Kuwait (‫الكويت‬‎)",
41741         "kw",
41742         "965"
41743       ],
41744       [
41745         "Kyrgyzstan (Кыргызстан)",
41746         "kg",
41747         "996"
41748       ],
41749       [
41750         "Laos (ລາວ)",
41751         "la",
41752         "856"
41753       ],
41754       [
41755         "Latvia (Latvija)",
41756         "lv",
41757         "371"
41758       ],
41759       [
41760         "Lebanon (‫لبنان‬‎)",
41761         "lb",
41762         "961"
41763       ],
41764       [
41765         "Lesotho",
41766         "ls",
41767         "266"
41768       ],
41769       [
41770         "Liberia",
41771         "lr",
41772         "231"
41773       ],
41774       [
41775         "Libya (‫ليبيا‬‎)",
41776         "ly",
41777         "218"
41778       ],
41779       [
41780         "Liechtenstein",
41781         "li",
41782         "423"
41783       ],
41784       [
41785         "Lithuania (Lietuva)",
41786         "lt",
41787         "370"
41788       ],
41789       [
41790         "Luxembourg",
41791         "lu",
41792         "352"
41793       ],
41794       [
41795         "Macau (澳門)",
41796         "mo",
41797         "853"
41798       ],
41799       [
41800         "Macedonia (FYROM) (Македонија)",
41801         "mk",
41802         "389"
41803       ],
41804       [
41805         "Madagascar (Madagasikara)",
41806         "mg",
41807         "261"
41808       ],
41809       [
41810         "Malawi",
41811         "mw",
41812         "265"
41813       ],
41814       [
41815         "Malaysia",
41816         "my",
41817         "60"
41818       ],
41819       [
41820         "Maldives",
41821         "mv",
41822         "960"
41823       ],
41824       [
41825         "Mali",
41826         "ml",
41827         "223"
41828       ],
41829       [
41830         "Malta",
41831         "mt",
41832         "356"
41833       ],
41834       [
41835         "Marshall Islands",
41836         "mh",
41837         "692"
41838       ],
41839       [
41840         "Martinique",
41841         "mq",
41842         "596"
41843       ],
41844       [
41845         "Mauritania (‫موريتانيا‬‎)",
41846         "mr",
41847         "222"
41848       ],
41849       [
41850         "Mauritius (Moris)",
41851         "mu",
41852         "230"
41853       ],
41854       [
41855         "Mayotte",
41856         "yt",
41857         "262",
41858         1
41859       ],
41860       [
41861         "Mexico (México)",
41862         "mx",
41863         "52"
41864       ],
41865       [
41866         "Micronesia",
41867         "fm",
41868         "691"
41869       ],
41870       [
41871         "Moldova (Republica Moldova)",
41872         "md",
41873         "373"
41874       ],
41875       [
41876         "Monaco",
41877         "mc",
41878         "377"
41879       ],
41880       [
41881         "Mongolia (Монгол)",
41882         "mn",
41883         "976"
41884       ],
41885       [
41886         "Montenegro (Crna Gora)",
41887         "me",
41888         "382"
41889       ],
41890       [
41891         "Montserrat",
41892         "ms",
41893         "1664"
41894       ],
41895       [
41896         "Morocco (‫المغرب‬‎)",
41897         "ma",
41898         "212",
41899         0
41900       ],
41901       [
41902         "Mozambique (Moçambique)",
41903         "mz",
41904         "258"
41905       ],
41906       [
41907         "Myanmar (Burma) (မြန်မာ)",
41908         "mm",
41909         "95"
41910       ],
41911       [
41912         "Namibia (Namibië)",
41913         "na",
41914         "264"
41915       ],
41916       [
41917         "Nauru",
41918         "nr",
41919         "674"
41920       ],
41921       [
41922         "Nepal (नेपाल)",
41923         "np",
41924         "977"
41925       ],
41926       [
41927         "Netherlands (Nederland)",
41928         "nl",
41929         "31"
41930       ],
41931       [
41932         "New Caledonia (Nouvelle-Calédonie)",
41933         "nc",
41934         "687"
41935       ],
41936       [
41937         "New Zealand",
41938         "nz",
41939         "64"
41940       ],
41941       [
41942         "Nicaragua",
41943         "ni",
41944         "505"
41945       ],
41946       [
41947         "Niger (Nijar)",
41948         "ne",
41949         "227"
41950       ],
41951       [
41952         "Nigeria",
41953         "ng",
41954         "234"
41955       ],
41956       [
41957         "Niue",
41958         "nu",
41959         "683"
41960       ],
41961       [
41962         "Norfolk Island",
41963         "nf",
41964         "672"
41965       ],
41966       [
41967         "North Korea (조선 민주주의 인민 공화국)",
41968         "kp",
41969         "850"
41970       ],
41971       [
41972         "Northern Mariana Islands",
41973         "mp",
41974         "1670"
41975       ],
41976       [
41977         "Norway (Norge)",
41978         "no",
41979         "47",
41980         0
41981       ],
41982       [
41983         "Oman (‫عُمان‬‎)",
41984         "om",
41985         "968"
41986       ],
41987       [
41988         "Pakistan (‫پاکستان‬‎)",
41989         "pk",
41990         "92"
41991       ],
41992       [
41993         "Palau",
41994         "pw",
41995         "680"
41996       ],
41997       [
41998         "Palestine (‫فلسطين‬‎)",
41999         "ps",
42000         "970"
42001       ],
42002       [
42003         "Panama (Panamá)",
42004         "pa",
42005         "507"
42006       ],
42007       [
42008         "Papua New Guinea",
42009         "pg",
42010         "675"
42011       ],
42012       [
42013         "Paraguay",
42014         "py",
42015         "595"
42016       ],
42017       [
42018         "Peru (Perú)",
42019         "pe",
42020         "51"
42021       ],
42022       [
42023         "Philippines",
42024         "ph",
42025         "63"
42026       ],
42027       [
42028         "Poland (Polska)",
42029         "pl",
42030         "48"
42031       ],
42032       [
42033         "Portugal",
42034         "pt",
42035         "351"
42036       ],
42037       [
42038         "Puerto Rico",
42039         "pr",
42040         "1",
42041         3,
42042         ["787", "939"]
42043       ],
42044       [
42045         "Qatar (‫قطر‬‎)",
42046         "qa",
42047         "974"
42048       ],
42049       [
42050         "Réunion (La Réunion)",
42051         "re",
42052         "262",
42053         0
42054       ],
42055       [
42056         "Romania (România)",
42057         "ro",
42058         "40"
42059       ],
42060       [
42061         "Russia (Россия)",
42062         "ru",
42063         "7",
42064         0
42065       ],
42066       [
42067         "Rwanda",
42068         "rw",
42069         "250"
42070       ],
42071       [
42072         "Saint Barthélemy",
42073         "bl",
42074         "590",
42075         1
42076       ],
42077       [
42078         "Saint Helena",
42079         "sh",
42080         "290"
42081       ],
42082       [
42083         "Saint Kitts and Nevis",
42084         "kn",
42085         "1869"
42086       ],
42087       [
42088         "Saint Lucia",
42089         "lc",
42090         "1758"
42091       ],
42092       [
42093         "Saint Martin (Saint-Martin (partie française))",
42094         "mf",
42095         "590",
42096         2
42097       ],
42098       [
42099         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42100         "pm",
42101         "508"
42102       ],
42103       [
42104         "Saint Vincent and the Grenadines",
42105         "vc",
42106         "1784"
42107       ],
42108       [
42109         "Samoa",
42110         "ws",
42111         "685"
42112       ],
42113       [
42114         "San Marino",
42115         "sm",
42116         "378"
42117       ],
42118       [
42119         "São Tomé and Príncipe (São Tomé e Príncipe)",
42120         "st",
42121         "239"
42122       ],
42123       [
42124         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42125         "sa",
42126         "966"
42127       ],
42128       [
42129         "Senegal (Sénégal)",
42130         "sn",
42131         "221"
42132       ],
42133       [
42134         "Serbia (Србија)",
42135         "rs",
42136         "381"
42137       ],
42138       [
42139         "Seychelles",
42140         "sc",
42141         "248"
42142       ],
42143       [
42144         "Sierra Leone",
42145         "sl",
42146         "232"
42147       ],
42148       [
42149         "Singapore",
42150         "sg",
42151         "65"
42152       ],
42153       [
42154         "Sint Maarten",
42155         "sx",
42156         "1721"
42157       ],
42158       [
42159         "Slovakia (Slovensko)",
42160         "sk",
42161         "421"
42162       ],
42163       [
42164         "Slovenia (Slovenija)",
42165         "si",
42166         "386"
42167       ],
42168       [
42169         "Solomon Islands",
42170         "sb",
42171         "677"
42172       ],
42173       [
42174         "Somalia (Soomaaliya)",
42175         "so",
42176         "252"
42177       ],
42178       [
42179         "South Africa",
42180         "za",
42181         "27"
42182       ],
42183       [
42184         "South Korea (대한민국)",
42185         "kr",
42186         "82"
42187       ],
42188       [
42189         "South Sudan (‫جنوب السودان‬‎)",
42190         "ss",
42191         "211"
42192       ],
42193       [
42194         "Spain (España)",
42195         "es",
42196         "34"
42197       ],
42198       [
42199         "Sri Lanka (ශ්‍රී ලංකාව)",
42200         "lk",
42201         "94"
42202       ],
42203       [
42204         "Sudan (‫السودان‬‎)",
42205         "sd",
42206         "249"
42207       ],
42208       [
42209         "Suriname",
42210         "sr",
42211         "597"
42212       ],
42213       [
42214         "Svalbard and Jan Mayen",
42215         "sj",
42216         "47",
42217         1
42218       ],
42219       [
42220         "Swaziland",
42221         "sz",
42222         "268"
42223       ],
42224       [
42225         "Sweden (Sverige)",
42226         "se",
42227         "46"
42228       ],
42229       [
42230         "Switzerland (Schweiz)",
42231         "ch",
42232         "41"
42233       ],
42234       [
42235         "Syria (‫سوريا‬‎)",
42236         "sy",
42237         "963"
42238       ],
42239       [
42240         "Taiwan (台灣)",
42241         "tw",
42242         "886"
42243       ],
42244       [
42245         "Tajikistan",
42246         "tj",
42247         "992"
42248       ],
42249       [
42250         "Tanzania",
42251         "tz",
42252         "255"
42253       ],
42254       [
42255         "Thailand (ไทย)",
42256         "th",
42257         "66"
42258       ],
42259       [
42260         "Timor-Leste",
42261         "tl",
42262         "670"
42263       ],
42264       [
42265         "Togo",
42266         "tg",
42267         "228"
42268       ],
42269       [
42270         "Tokelau",
42271         "tk",
42272         "690"
42273       ],
42274       [
42275         "Tonga",
42276         "to",
42277         "676"
42278       ],
42279       [
42280         "Trinidad and Tobago",
42281         "tt",
42282         "1868"
42283       ],
42284       [
42285         "Tunisia (‫تونس‬‎)",
42286         "tn",
42287         "216"
42288       ],
42289       [
42290         "Turkey (Türkiye)",
42291         "tr",
42292         "90"
42293       ],
42294       [
42295         "Turkmenistan",
42296         "tm",
42297         "993"
42298       ],
42299       [
42300         "Turks and Caicos Islands",
42301         "tc",
42302         "1649"
42303       ],
42304       [
42305         "Tuvalu",
42306         "tv",
42307         "688"
42308       ],
42309       [
42310         "U.S. Virgin Islands",
42311         "vi",
42312         "1340"
42313       ],
42314       [
42315         "Uganda",
42316         "ug",
42317         "256"
42318       ],
42319       [
42320         "Ukraine (Україна)",
42321         "ua",
42322         "380"
42323       ],
42324       [
42325         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42326         "ae",
42327         "971"
42328       ],
42329       [
42330         "United Kingdom",
42331         "gb",
42332         "44",
42333         0
42334       ],
42335       [
42336         "United States",
42337         "us",
42338         "1",
42339         0
42340       ],
42341       [
42342         "Uruguay",
42343         "uy",
42344         "598"
42345       ],
42346       [
42347         "Uzbekistan (Oʻzbekiston)",
42348         "uz",
42349         "998"
42350       ],
42351       [
42352         "Vanuatu",
42353         "vu",
42354         "678"
42355       ],
42356       [
42357         "Vatican City (Città del Vaticano)",
42358         "va",
42359         "39",
42360         1
42361       ],
42362       [
42363         "Venezuela",
42364         "ve",
42365         "58"
42366       ],
42367       [
42368         "Vietnam (Việt Nam)",
42369         "vn",
42370         "84"
42371       ],
42372       [
42373         "Wallis and Futuna (Wallis-et-Futuna)",
42374         "wf",
42375         "681"
42376       ],
42377       [
42378         "Western Sahara (‫الصحراء الغربية‬‎)",
42379         "eh",
42380         "212",
42381         1
42382       ],
42383       [
42384         "Yemen (‫اليمن‬‎)",
42385         "ye",
42386         "967"
42387       ],
42388       [
42389         "Zambia",
42390         "zm",
42391         "260"
42392       ],
42393       [
42394         "Zimbabwe",
42395         "zw",
42396         "263"
42397       ],
42398       [
42399         "Åland Islands",
42400         "ax",
42401         "358",
42402         1
42403       ]
42404   ];
42405   
42406   return d;
42407 }/**
42408 *    This script refer to:
42409 *    Title: International Telephone Input
42410 *    Author: Jack O'Connor
42411 *    Code version:  v12.1.12
42412 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42413 **/
42414
42415 /**
42416  * @class Roo.bootstrap.PhoneInput
42417  * @extends Roo.bootstrap.TriggerField
42418  * An input with International dial-code selection
42419  
42420  * @cfg {String} defaultDialCode default '+852'
42421  * @cfg {Array} preferedCountries default []
42422   
42423  * @constructor
42424  * Create a new PhoneInput.
42425  * @param {Object} config Configuration options
42426  */
42427
42428 Roo.bootstrap.PhoneInput = function(config) {
42429     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42430 };
42431
42432 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42433         
42434         listWidth: undefined,
42435         
42436         selectedClass: 'active',
42437         
42438         invalidClass : "has-warning",
42439         
42440         validClass: 'has-success',
42441         
42442         allowed: '0123456789',
42443         
42444         max_length: 15,
42445         
42446         /**
42447          * @cfg {String} defaultDialCode The default dial code when initializing the input
42448          */
42449         defaultDialCode: '+852',
42450         
42451         /**
42452          * @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
42453          */
42454         preferedCountries: false,
42455         
42456         getAutoCreate : function()
42457         {
42458             var data = Roo.bootstrap.PhoneInputData();
42459             var align = this.labelAlign || this.parentLabelAlign();
42460             var id = Roo.id();
42461             
42462             this.allCountries = [];
42463             this.dialCodeMapping = [];
42464             
42465             for (var i = 0; i < data.length; i++) {
42466               var c = data[i];
42467               this.allCountries[i] = {
42468                 name: c[0],
42469                 iso2: c[1],
42470                 dialCode: c[2],
42471                 priority: c[3] || 0,
42472                 areaCodes: c[4] || null
42473               };
42474               this.dialCodeMapping[c[2]] = {
42475                   name: c[0],
42476                   iso2: c[1],
42477                   priority: c[3] || 0,
42478                   areaCodes: c[4] || null
42479               };
42480             }
42481             
42482             var cfg = {
42483                 cls: 'form-group',
42484                 cn: []
42485             };
42486             
42487             var input =  {
42488                 tag: 'input',
42489                 id : id,
42490                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42491                 maxlength: this.max_length,
42492                 cls : 'form-control tel-input',
42493                 autocomplete: 'new-password'
42494             };
42495             
42496             var hiddenInput = {
42497                 tag: 'input',
42498                 type: 'hidden',
42499                 cls: 'hidden-tel-input'
42500             };
42501             
42502             if (this.name) {
42503                 hiddenInput.name = this.name;
42504             }
42505             
42506             if (this.disabled) {
42507                 input.disabled = true;
42508             }
42509             
42510             var flag_container = {
42511                 tag: 'div',
42512                 cls: 'flag-box',
42513                 cn: [
42514                     {
42515                         tag: 'div',
42516                         cls: 'flag'
42517                     },
42518                     {
42519                         tag: 'div',
42520                         cls: 'caret'
42521                     }
42522                 ]
42523             };
42524             
42525             var box = {
42526                 tag: 'div',
42527                 cls: this.hasFeedback ? 'has-feedback' : '',
42528                 cn: [
42529                     hiddenInput,
42530                     input,
42531                     {
42532                         tag: 'input',
42533                         cls: 'dial-code-holder',
42534                         disabled: true
42535                     }
42536                 ]
42537             };
42538             
42539             var container = {
42540                 cls: 'roo-select2-container input-group',
42541                 cn: [
42542                     flag_container,
42543                     box
42544                 ]
42545             };
42546             
42547             if (this.fieldLabel.length) {
42548                 var indicator = {
42549                     tag: 'i',
42550                     tooltip: 'This field is required'
42551                 };
42552                 
42553                 var label = {
42554                     tag: 'label',
42555                     'for':  id,
42556                     cls: 'control-label',
42557                     cn: []
42558                 };
42559                 
42560                 var label_text = {
42561                     tag: 'span',
42562                     html: this.fieldLabel
42563                 };
42564                 
42565                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42566                 label.cn = [
42567                     indicator,
42568                     label_text
42569                 ];
42570                 
42571                 if(this.indicatorpos == 'right') {
42572                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42573                     label.cn = [
42574                         label_text,
42575                         indicator
42576                     ];
42577                 }
42578                 
42579                 if(align == 'left') {
42580                     container = {
42581                         tag: 'div',
42582                         cn: [
42583                             container
42584                         ]
42585                     };
42586                     
42587                     if(this.labelWidth > 12){
42588                         label.style = "width: " + this.labelWidth + 'px';
42589                     }
42590                     if(this.labelWidth < 13 && this.labelmd == 0){
42591                         this.labelmd = this.labelWidth;
42592                     }
42593                     if(this.labellg > 0){
42594                         label.cls += ' col-lg-' + this.labellg;
42595                         input.cls += ' col-lg-' + (12 - this.labellg);
42596                     }
42597                     if(this.labelmd > 0){
42598                         label.cls += ' col-md-' + this.labelmd;
42599                         container.cls += ' col-md-' + (12 - this.labelmd);
42600                     }
42601                     if(this.labelsm > 0){
42602                         label.cls += ' col-sm-' + this.labelsm;
42603                         container.cls += ' col-sm-' + (12 - this.labelsm);
42604                     }
42605                     if(this.labelxs > 0){
42606                         label.cls += ' col-xs-' + this.labelxs;
42607                         container.cls += ' col-xs-' + (12 - this.labelxs);
42608                     }
42609                 }
42610             }
42611             
42612             cfg.cn = [
42613                 label,
42614                 container
42615             ];
42616             
42617             var settings = this;
42618             
42619             ['xs','sm','md','lg'].map(function(size){
42620                 if (settings[size]) {
42621                     cfg.cls += ' col-' + size + '-' + settings[size];
42622                 }
42623             });
42624             
42625             this.store = new Roo.data.Store({
42626                 proxy : new Roo.data.MemoryProxy({}),
42627                 reader : new Roo.data.JsonReader({
42628                     fields : [
42629                         {
42630                             'name' : 'name',
42631                             'type' : 'string'
42632                         },
42633                         {
42634                             'name' : 'iso2',
42635                             'type' : 'string'
42636                         },
42637                         {
42638                             'name' : 'dialCode',
42639                             'type' : 'string'
42640                         },
42641                         {
42642                             'name' : 'priority',
42643                             'type' : 'string'
42644                         },
42645                         {
42646                             'name' : 'areaCodes',
42647                             'type' : 'string'
42648                         }
42649                     ]
42650                 })
42651             });
42652             
42653             if(!this.preferedCountries) {
42654                 this.preferedCountries = [
42655                     'hk',
42656                     'gb',
42657                     'us'
42658                 ];
42659             }
42660             
42661             var p = this.preferedCountries.reverse();
42662             
42663             if(p) {
42664                 for (var i = 0; i < p.length; i++) {
42665                     for (var j = 0; j < this.allCountries.length; j++) {
42666                         if(this.allCountries[j].iso2 == p[i]) {
42667                             var t = this.allCountries[j];
42668                             this.allCountries.splice(j,1);
42669                             this.allCountries.unshift(t);
42670                         }
42671                     } 
42672                 }
42673             }
42674             
42675             this.store.proxy.data = {
42676                 success: true,
42677                 data: this.allCountries
42678             };
42679             
42680             return cfg;
42681         },
42682         
42683         initEvents : function()
42684         {
42685             this.createList();
42686             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42687             
42688             this.indicator = this.indicatorEl();
42689             this.flag = this.flagEl();
42690             this.dialCodeHolder = this.dialCodeHolderEl();
42691             
42692             this.trigger = this.el.select('div.flag-box',true).first();
42693             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42694             
42695             var _this = this;
42696             
42697             (function(){
42698                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42699                 _this.list.setWidth(lw);
42700             }).defer(100);
42701             
42702             this.list.on('mouseover', this.onViewOver, this);
42703             this.list.on('mousemove', this.onViewMove, this);
42704             this.inputEl().on("keyup", this.onKeyUp, this);
42705             this.inputEl().on("keypress", this.onKeyPress, this);
42706             
42707             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42708
42709             this.view = new Roo.View(this.list, this.tpl, {
42710                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42711             });
42712             
42713             this.view.on('click', this.onViewClick, this);
42714             this.setValue(this.defaultDialCode);
42715         },
42716         
42717         onTriggerClick : function(e)
42718         {
42719             Roo.log('trigger click');
42720             if(this.disabled){
42721                 return;
42722             }
42723             
42724             if(this.isExpanded()){
42725                 this.collapse();
42726                 this.hasFocus = false;
42727             }else {
42728                 this.store.load({});
42729                 this.hasFocus = true;
42730                 this.expand();
42731             }
42732         },
42733         
42734         isExpanded : function()
42735         {
42736             return this.list.isVisible();
42737         },
42738         
42739         collapse : function()
42740         {
42741             if(!this.isExpanded()){
42742                 return;
42743             }
42744             this.list.hide();
42745             Roo.get(document).un('mousedown', this.collapseIf, this);
42746             Roo.get(document).un('mousewheel', this.collapseIf, this);
42747             this.fireEvent('collapse', this);
42748             this.validate();
42749         },
42750         
42751         expand : function()
42752         {
42753             Roo.log('expand');
42754
42755             if(this.isExpanded() || !this.hasFocus){
42756                 return;
42757             }
42758             
42759             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42760             this.list.setWidth(lw);
42761             
42762             this.list.show();
42763             this.restrictHeight();
42764             
42765             Roo.get(document).on('mousedown', this.collapseIf, this);
42766             Roo.get(document).on('mousewheel', this.collapseIf, this);
42767             
42768             this.fireEvent('expand', this);
42769         },
42770         
42771         restrictHeight : function()
42772         {
42773             this.list.alignTo(this.inputEl(), this.listAlign);
42774             this.list.alignTo(this.inputEl(), this.listAlign);
42775         },
42776         
42777         onViewOver : function(e, t)
42778         {
42779             if(this.inKeyMode){
42780                 return;
42781             }
42782             var item = this.view.findItemFromChild(t);
42783             
42784             if(item){
42785                 var index = this.view.indexOf(item);
42786                 this.select(index, false);
42787             }
42788         },
42789
42790         // private
42791         onViewClick : function(view, doFocus, el, e)
42792         {
42793             var index = this.view.getSelectedIndexes()[0];
42794             
42795             var r = this.store.getAt(index);
42796             
42797             if(r){
42798                 this.onSelect(r, index);
42799             }
42800             if(doFocus !== false && !this.blockFocus){
42801                 this.inputEl().focus();
42802             }
42803         },
42804         
42805         onViewMove : function(e, t)
42806         {
42807             this.inKeyMode = false;
42808         },
42809         
42810         select : function(index, scrollIntoView)
42811         {
42812             this.selectedIndex = index;
42813             this.view.select(index);
42814             if(scrollIntoView !== false){
42815                 var el = this.view.getNode(index);
42816                 if(el){
42817                     this.list.scrollChildIntoView(el, false);
42818                 }
42819             }
42820         },
42821         
42822         createList : function()
42823         {
42824             this.list = Roo.get(document.body).createChild({
42825                 tag: 'ul',
42826                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42827                 style: 'display:none'
42828             });
42829             
42830             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42831         },
42832         
42833         collapseIf : function(e)
42834         {
42835             var in_combo  = e.within(this.el);
42836             var in_list =  e.within(this.list);
42837             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42838             
42839             if (in_combo || in_list || is_list) {
42840                 return;
42841             }
42842             this.collapse();
42843         },
42844         
42845         onSelect : function(record, index)
42846         {
42847             if(this.fireEvent('beforeselect', this, record, index) !== false){
42848                 
42849                 this.setFlagClass(record.data.iso2);
42850                 this.setDialCode(record.data.dialCode);
42851                 this.hasFocus = false;
42852                 this.collapse();
42853                 this.fireEvent('select', this, record, index);
42854             }
42855         },
42856         
42857         flagEl : function()
42858         {
42859             var flag = this.el.select('div.flag',true).first();
42860             if(!flag){
42861                 return false;
42862             }
42863             return flag;
42864         },
42865         
42866         dialCodeHolderEl : function()
42867         {
42868             var d = this.el.select('input.dial-code-holder',true).first();
42869             if(!d){
42870                 return false;
42871             }
42872             return d;
42873         },
42874         
42875         setDialCode : function(v)
42876         {
42877             this.dialCodeHolder.dom.value = '+'+v;
42878         },
42879         
42880         setFlagClass : function(n)
42881         {
42882             this.flag.dom.className = 'flag '+n;
42883         },
42884         
42885         getValue : function()
42886         {
42887             var v = this.inputEl().getValue();
42888             if(this.dialCodeHolder) {
42889                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42890             }
42891             return v;
42892         },
42893         
42894         setValue : function(v)
42895         {
42896             var d = this.getDialCode(v);
42897             
42898             //invalid dial code
42899             if(v.length == 0 || !d || d.length == 0) {
42900                 if(this.rendered){
42901                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42902                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42903                 }
42904                 return;
42905             }
42906             
42907             //valid dial code
42908             this.setFlagClass(this.dialCodeMapping[d].iso2);
42909             this.setDialCode(d);
42910             this.inputEl().dom.value = v.replace('+'+d,'');
42911             this.hiddenEl().dom.value = this.getValue();
42912             
42913             this.validate();
42914         },
42915         
42916         getDialCode : function(v)
42917         {
42918             v = v ||  '';
42919             
42920             if (v.length == 0) {
42921                 return this.dialCodeHolder.dom.value;
42922             }
42923             
42924             var dialCode = "";
42925             if (v.charAt(0) != "+") {
42926                 return false;
42927             }
42928             var numericChars = "";
42929             for (var i = 1; i < v.length; i++) {
42930               var c = v.charAt(i);
42931               if (!isNaN(c)) {
42932                 numericChars += c;
42933                 if (this.dialCodeMapping[numericChars]) {
42934                   dialCode = v.substr(1, i);
42935                 }
42936                 if (numericChars.length == 4) {
42937                   break;
42938                 }
42939               }
42940             }
42941             return dialCode;
42942         },
42943         
42944         reset : function()
42945         {
42946             this.setValue(this.defaultDialCode);
42947             this.validate();
42948         },
42949         
42950         hiddenEl : function()
42951         {
42952             return this.el.select('input.hidden-tel-input',true).first();
42953         },
42954         
42955         // after setting val
42956         onKeyUp : function(e){
42957             this.setValue(this.getValue());
42958         },
42959         
42960         onKeyPress : function(e){
42961             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42962                 e.stopEvent();
42963             }
42964         }
42965         
42966 });
42967 /**
42968  * @class Roo.bootstrap.MoneyField
42969  * @extends Roo.bootstrap.ComboBox
42970  * Bootstrap MoneyField class
42971  * 
42972  * @constructor
42973  * Create a new MoneyField.
42974  * @param {Object} config Configuration options
42975  */
42976
42977 Roo.bootstrap.MoneyField = function(config) {
42978     
42979     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42980     
42981 };
42982
42983 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42984     
42985     /**
42986      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42987      */
42988     allowDecimals : true,
42989     /**
42990      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42991      */
42992     decimalSeparator : ".",
42993     /**
42994      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42995      */
42996     decimalPrecision : 0,
42997     /**
42998      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42999      */
43000     allowNegative : true,
43001     /**
43002      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43003      */
43004     allowZero: true,
43005     /**
43006      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43007      */
43008     minValue : Number.NEGATIVE_INFINITY,
43009     /**
43010      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43011      */
43012     maxValue : Number.MAX_VALUE,
43013     /**
43014      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43015      */
43016     minText : "The minimum value for this field is {0}",
43017     /**
43018      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43019      */
43020     maxText : "The maximum value for this field is {0}",
43021     /**
43022      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43023      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43024      */
43025     nanText : "{0} is not a valid number",
43026     /**
43027      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43028      */
43029     castInt : true,
43030     /**
43031      * @cfg {String} defaults currency of the MoneyField
43032      * value should be in lkey
43033      */
43034     defaultCurrency : false,
43035     /**
43036      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43037      */
43038     thousandsDelimiter : false,
43039     /**
43040      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43041      */
43042     max_length: false,
43043     
43044     inputlg : 9,
43045     inputmd : 9,
43046     inputsm : 9,
43047     inputxs : 6,
43048     
43049     store : false,
43050     
43051     getAutoCreate : function()
43052     {
43053         var align = this.labelAlign || this.parentLabelAlign();
43054         
43055         var id = Roo.id();
43056
43057         var cfg = {
43058             cls: 'form-group',
43059             cn: []
43060         };
43061
43062         var input =  {
43063             tag: 'input',
43064             id : id,
43065             cls : 'form-control roo-money-amount-input',
43066             autocomplete: 'new-password'
43067         };
43068         
43069         var hiddenInput = {
43070             tag: 'input',
43071             type: 'hidden',
43072             id: Roo.id(),
43073             cls: 'hidden-number-input'
43074         };
43075         
43076         if(this.max_length) {
43077             input.maxlength = this.max_length; 
43078         }
43079         
43080         if (this.name) {
43081             hiddenInput.name = this.name;
43082         }
43083
43084         if (this.disabled) {
43085             input.disabled = true;
43086         }
43087
43088         var clg = 12 - this.inputlg;
43089         var cmd = 12 - this.inputmd;
43090         var csm = 12 - this.inputsm;
43091         var cxs = 12 - this.inputxs;
43092         
43093         var container = {
43094             tag : 'div',
43095             cls : 'row roo-money-field',
43096             cn : [
43097                 {
43098                     tag : 'div',
43099                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43100                     cn : [
43101                         {
43102                             tag : 'div',
43103                             cls: 'roo-select2-container input-group',
43104                             cn: [
43105                                 {
43106                                     tag : 'input',
43107                                     cls : 'form-control roo-money-currency-input',
43108                                     autocomplete: 'new-password',
43109                                     readOnly : 1,
43110                                     name : this.currencyName
43111                                 },
43112                                 {
43113                                     tag :'span',
43114                                     cls : 'input-group-addon',
43115                                     cn : [
43116                                         {
43117                                             tag: 'span',
43118                                             cls: 'caret'
43119                                         }
43120                                     ]
43121                                 }
43122                             ]
43123                         }
43124                     ]
43125                 },
43126                 {
43127                     tag : 'div',
43128                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43129                     cn : [
43130                         {
43131                             tag: 'div',
43132                             cls: this.hasFeedback ? 'has-feedback' : '',
43133                             cn: [
43134                                 input
43135                             ]
43136                         }
43137                     ]
43138                 }
43139             ]
43140             
43141         };
43142         
43143         if (this.fieldLabel.length) {
43144             var indicator = {
43145                 tag: 'i',
43146                 tooltip: 'This field is required'
43147             };
43148
43149             var label = {
43150                 tag: 'label',
43151                 'for':  id,
43152                 cls: 'control-label',
43153                 cn: []
43154             };
43155
43156             var label_text = {
43157                 tag: 'span',
43158                 html: this.fieldLabel
43159             };
43160
43161             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43162             label.cn = [
43163                 indicator,
43164                 label_text
43165             ];
43166
43167             if(this.indicatorpos == 'right') {
43168                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43169                 label.cn = [
43170                     label_text,
43171                     indicator
43172                 ];
43173             }
43174
43175             if(align == 'left') {
43176                 container = {
43177                     tag: 'div',
43178                     cn: [
43179                         container
43180                     ]
43181                 };
43182
43183                 if(this.labelWidth > 12){
43184                     label.style = "width: " + this.labelWidth + 'px';
43185                 }
43186                 if(this.labelWidth < 13 && this.labelmd == 0){
43187                     this.labelmd = this.labelWidth;
43188                 }
43189                 if(this.labellg > 0){
43190                     label.cls += ' col-lg-' + this.labellg;
43191                     input.cls += ' col-lg-' + (12 - this.labellg);
43192                 }
43193                 if(this.labelmd > 0){
43194                     label.cls += ' col-md-' + this.labelmd;
43195                     container.cls += ' col-md-' + (12 - this.labelmd);
43196                 }
43197                 if(this.labelsm > 0){
43198                     label.cls += ' col-sm-' + this.labelsm;
43199                     container.cls += ' col-sm-' + (12 - this.labelsm);
43200                 }
43201                 if(this.labelxs > 0){
43202                     label.cls += ' col-xs-' + this.labelxs;
43203                     container.cls += ' col-xs-' + (12 - this.labelxs);
43204                 }
43205             }
43206         }
43207
43208         cfg.cn = [
43209             label,
43210             container,
43211             hiddenInput
43212         ];
43213         
43214         var settings = this;
43215
43216         ['xs','sm','md','lg'].map(function(size){
43217             if (settings[size]) {
43218                 cfg.cls += ' col-' + size + '-' + settings[size];
43219             }
43220         });
43221         
43222         return cfg;
43223     },
43224     
43225     initEvents : function()
43226     {
43227         this.indicator = this.indicatorEl();
43228         
43229         this.initCurrencyEvent();
43230         
43231         this.initNumberEvent();
43232     },
43233     
43234     initCurrencyEvent : function()
43235     {
43236         if (!this.store) {
43237             throw "can not find store for combo";
43238         }
43239         
43240         this.store = Roo.factory(this.store, Roo.data);
43241         this.store.parent = this;
43242         
43243         this.createList();
43244         
43245         this.triggerEl = this.el.select('.input-group-addon', true).first();
43246         
43247         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43248         
43249         var _this = this;
43250         
43251         (function(){
43252             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43253             _this.list.setWidth(lw);
43254         }).defer(100);
43255         
43256         this.list.on('mouseover', this.onViewOver, this);
43257         this.list.on('mousemove', this.onViewMove, this);
43258         this.list.on('scroll', this.onViewScroll, this);
43259         
43260         if(!this.tpl){
43261             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43262         }
43263         
43264         this.view = new Roo.View(this.list, this.tpl, {
43265             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43266         });
43267         
43268         this.view.on('click', this.onViewClick, this);
43269         
43270         this.store.on('beforeload', this.onBeforeLoad, this);
43271         this.store.on('load', this.onLoad, this);
43272         this.store.on('loadexception', this.onLoadException, this);
43273         
43274         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43275             "up" : function(e){
43276                 this.inKeyMode = true;
43277                 this.selectPrev();
43278             },
43279
43280             "down" : function(e){
43281                 if(!this.isExpanded()){
43282                     this.onTriggerClick();
43283                 }else{
43284                     this.inKeyMode = true;
43285                     this.selectNext();
43286                 }
43287             },
43288
43289             "enter" : function(e){
43290                 this.collapse();
43291                 
43292                 if(this.fireEvent("specialkey", this, e)){
43293                     this.onViewClick(false);
43294                 }
43295                 
43296                 return true;
43297             },
43298
43299             "esc" : function(e){
43300                 this.collapse();
43301             },
43302
43303             "tab" : function(e){
43304                 this.collapse();
43305                 
43306                 if(this.fireEvent("specialkey", this, e)){
43307                     this.onViewClick(false);
43308                 }
43309                 
43310                 return true;
43311             },
43312
43313             scope : this,
43314
43315             doRelay : function(foo, bar, hname){
43316                 if(hname == 'down' || this.scope.isExpanded()){
43317                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43318                 }
43319                 return true;
43320             },
43321
43322             forceKeyDown: true
43323         });
43324         
43325         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43326         
43327     },
43328     
43329     initNumberEvent : function(e)
43330     {
43331         this.inputEl().on("keydown" , this.fireKey,  this);
43332         this.inputEl().on("focus", this.onFocus,  this);
43333         this.inputEl().on("blur", this.onBlur,  this);
43334         
43335         this.inputEl().relayEvent('keyup', this);
43336         
43337         if(this.indicator){
43338             this.indicator.addClass('invisible');
43339         }
43340  
43341         this.originalValue = this.getValue();
43342         
43343         if(this.validationEvent == 'keyup'){
43344             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43345             this.inputEl().on('keyup', this.filterValidation, this);
43346         }
43347         else if(this.validationEvent !== false){
43348             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43349         }
43350         
43351         if(this.selectOnFocus){
43352             this.on("focus", this.preFocus, this);
43353             
43354         }
43355         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43356             this.inputEl().on("keypress", this.filterKeys, this);
43357         } else {
43358             this.inputEl().relayEvent('keypress', this);
43359         }
43360         
43361         var allowed = "0123456789";
43362         
43363         if(this.allowDecimals){
43364             allowed += this.decimalSeparator;
43365         }
43366         
43367         if(this.allowNegative){
43368             allowed += "-";
43369         }
43370         
43371         if(this.thousandsDelimiter) {
43372             allowed += ",";
43373         }
43374         
43375         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43376         
43377         var keyPress = function(e){
43378             
43379             var k = e.getKey();
43380             
43381             var c = e.getCharCode();
43382             
43383             if(
43384                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43385                     allowed.indexOf(String.fromCharCode(c)) === -1
43386             ){
43387                 e.stopEvent();
43388                 return;
43389             }
43390             
43391             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43392                 return;
43393             }
43394             
43395             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43396                 e.stopEvent();
43397             }
43398         };
43399         
43400         this.inputEl().on("keypress", keyPress, this);
43401         
43402     },
43403     
43404     onTriggerClick : function(e)
43405     {   
43406         if(this.disabled){
43407             return;
43408         }
43409         
43410         this.page = 0;
43411         this.loadNext = false;
43412         
43413         if(this.isExpanded()){
43414             this.collapse();
43415             return;
43416         }
43417         
43418         this.hasFocus = true;
43419         
43420         if(this.triggerAction == 'all') {
43421             this.doQuery(this.allQuery, true);
43422             return;
43423         }
43424         
43425         this.doQuery(this.getRawValue());
43426     },
43427     
43428     getCurrency : function()
43429     {   
43430         var v = this.currencyEl().getValue();
43431         
43432         return v;
43433     },
43434     
43435     restrictHeight : function()
43436     {
43437         this.list.alignTo(this.currencyEl(), this.listAlign);
43438         this.list.alignTo(this.currencyEl(), this.listAlign);
43439     },
43440     
43441     onViewClick : function(view, doFocus, el, e)
43442     {
43443         var index = this.view.getSelectedIndexes()[0];
43444         
43445         var r = this.store.getAt(index);
43446         
43447         if(r){
43448             this.onSelect(r, index);
43449         }
43450     },
43451     
43452     onSelect : function(record, index){
43453         
43454         if(this.fireEvent('beforeselect', this, record, index) !== false){
43455         
43456             this.setFromCurrencyData(index > -1 ? record.data : false);
43457             
43458             this.collapse();
43459             
43460             this.fireEvent('select', this, record, index);
43461         }
43462     },
43463     
43464     setFromCurrencyData : function(o)
43465     {
43466         var currency = '';
43467         
43468         this.lastCurrency = o;
43469         
43470         if (this.currencyField) {
43471             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43472         } else {
43473             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43474         }
43475         
43476         this.lastSelectionText = currency;
43477         
43478         //setting default currency
43479         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43480             this.setCurrency(this.defaultCurrency);
43481             return;
43482         }
43483         
43484         this.setCurrency(currency);
43485     },
43486     
43487     setFromData : function(o)
43488     {
43489         var c = {};
43490         
43491         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43492         
43493         this.setFromCurrencyData(c);
43494         
43495         var value = '';
43496         
43497         if (this.name) {
43498             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43499         } else {
43500             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43501         }
43502         
43503         this.setValue(value);
43504         
43505     },
43506     
43507     setCurrency : function(v)
43508     {   
43509         this.currencyValue = v;
43510         
43511         if(this.rendered){
43512             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43513             this.validate();
43514         }
43515     },
43516     
43517     setValue : function(v)
43518     {
43519         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43520         
43521         this.value = v;
43522         
43523         if(this.rendered){
43524             
43525             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43526             
43527             this.inputEl().dom.value = (v == '') ? '' :
43528                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43529             
43530             if(!this.allowZero && v === '0') {
43531                 this.hiddenEl().dom.value = '';
43532                 this.inputEl().dom.value = '';
43533             }
43534             
43535             this.validate();
43536         }
43537     },
43538     
43539     getRawValue : function()
43540     {
43541         var v = this.inputEl().getValue();
43542         
43543         return v;
43544     },
43545     
43546     getValue : function()
43547     {
43548         return this.fixPrecision(this.parseValue(this.getRawValue()));
43549     },
43550     
43551     parseValue : function(value)
43552     {
43553         if(this.thousandsDelimiter) {
43554             value += "";
43555             r = new RegExp(",", "g");
43556             value = value.replace(r, "");
43557         }
43558         
43559         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43560         return isNaN(value) ? '' : value;
43561         
43562     },
43563     
43564     fixPrecision : function(value)
43565     {
43566         if(this.thousandsDelimiter) {
43567             value += "";
43568             r = new RegExp(",", "g");
43569             value = value.replace(r, "");
43570         }
43571         
43572         var nan = isNaN(value);
43573         
43574         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43575             return nan ? '' : value;
43576         }
43577         return parseFloat(value).toFixed(this.decimalPrecision);
43578     },
43579     
43580     decimalPrecisionFcn : function(v)
43581     {
43582         return Math.floor(v);
43583     },
43584     
43585     validateValue : function(value)
43586     {
43587         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43588             return false;
43589         }
43590         
43591         var num = this.parseValue(value);
43592         
43593         if(isNaN(num)){
43594             this.markInvalid(String.format(this.nanText, value));
43595             return false;
43596         }
43597         
43598         if(num < this.minValue){
43599             this.markInvalid(String.format(this.minText, this.minValue));
43600             return false;
43601         }
43602         
43603         if(num > this.maxValue){
43604             this.markInvalid(String.format(this.maxText, this.maxValue));
43605             return false;
43606         }
43607         
43608         return true;
43609     },
43610     
43611     validate : function()
43612     {
43613         if(this.disabled || this.allowBlank){
43614             this.markValid();
43615             return true;
43616         }
43617         
43618         var currency = this.getCurrency();
43619         
43620         if(this.validateValue(this.getRawValue()) && currency.length){
43621             this.markValid();
43622             return true;
43623         }
43624         
43625         this.markInvalid();
43626         return false;
43627     },
43628     
43629     getName: function()
43630     {
43631         return this.name;
43632     },
43633     
43634     beforeBlur : function()
43635     {
43636         if(!this.castInt){
43637             return;
43638         }
43639         
43640         var v = this.parseValue(this.getRawValue());
43641         
43642         if(v || v == 0){
43643             this.setValue(v);
43644         }
43645     },
43646     
43647     onBlur : function()
43648     {
43649         this.beforeBlur();
43650         
43651         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43652             //this.el.removeClass(this.focusClass);
43653         }
43654         
43655         this.hasFocus = false;
43656         
43657         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43658             this.validate();
43659         }
43660         
43661         var v = this.getValue();
43662         
43663         if(String(v) !== String(this.startValue)){
43664             this.fireEvent('change', this, v, this.startValue);
43665         }
43666         
43667         this.fireEvent("blur", this);
43668     },
43669     
43670     inputEl : function()
43671     {
43672         return this.el.select('.roo-money-amount-input', true).first();
43673     },
43674     
43675     currencyEl : function()
43676     {
43677         return this.el.select('.roo-money-currency-input', true).first();
43678     },
43679     
43680     hiddenEl : function()
43681     {
43682         return this.el.select('input.hidden-number-input',true).first();
43683     }
43684     
43685 });/**
43686  * @class Roo.bootstrap.BezierSignature
43687  * @extends Roo.bootstrap.Component
43688  * Bootstrap BezierSignature class
43689  * This script refer to:
43690  *    Title: Signature Pad
43691  *    Author: szimek
43692  *    Availability: https://github.com/szimek/signature_pad
43693  *
43694  * @constructor
43695  * Create a new BezierSignature
43696  * @param {Object} config The config object
43697  */
43698
43699 Roo.bootstrap.BezierSignature = function(config){
43700     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43701     this.addEvents({
43702         "resize" : true
43703     });
43704 };
43705
43706 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43707 {
43708      
43709     curve_data: [],
43710     
43711     is_empty: true,
43712     
43713     mouse_btn_down: true,
43714     
43715     /**
43716      * @cfg {int} canvas height
43717      */
43718     canvas_height: '200px',
43719     
43720     /**
43721      * @cfg {float|function} Radius of a single dot.
43722      */ 
43723     dot_size: false,
43724     
43725     /**
43726      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43727      */
43728     min_width: 0.5,
43729     
43730     /**
43731      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43732      */
43733     max_width: 2.5,
43734     
43735     /**
43736      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43737      */
43738     throttle: 16,
43739     
43740     /**
43741      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43742      */
43743     min_distance: 5,
43744     
43745     /**
43746      * @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.
43747      */
43748     bg_color: 'rgba(0, 0, 0, 0)',
43749     
43750     /**
43751      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43752      */
43753     dot_color: 'black',
43754     
43755     /**
43756      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43757      */ 
43758     velocity_filter_weight: 0.7,
43759     
43760     /**
43761      * @cfg {function} Callback when stroke begin. 
43762      */
43763     onBegin: false,
43764     
43765     /**
43766      * @cfg {function} Callback when stroke end.
43767      */
43768     onEnd: false,
43769     
43770     getAutoCreate : function()
43771     {
43772         var cls = 'roo-signature column';
43773         
43774         if(this.cls){
43775             cls += ' ' + this.cls;
43776         }
43777         
43778         var col_sizes = [
43779             'lg',
43780             'md',
43781             'sm',
43782             'xs'
43783         ];
43784         
43785         for(var i = 0; i < col_sizes.length; i++) {
43786             if(this[col_sizes[i]]) {
43787                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43788             }
43789         }
43790         
43791         var cfg = {
43792             tag: 'div',
43793             cls: cls,
43794             cn: [
43795                 {
43796                     tag: 'div',
43797                     cls: 'roo-signature-body',
43798                     cn: [
43799                         {
43800                             tag: 'canvas',
43801                             cls: 'roo-signature-body-canvas',
43802                             height: this.canvas_height,
43803                             width: this.canvas_width
43804                         }
43805                     ]
43806                 },
43807                 {
43808                     tag: 'input',
43809                     type: 'file',
43810                     style: 'display: none'
43811                 }
43812             ]
43813         };
43814         
43815         return cfg;
43816     },
43817     
43818     initEvents: function() 
43819     {
43820         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43821         
43822         var canvas = this.canvasEl();
43823         
43824         // mouse && touch event swapping...
43825         canvas.dom.style.touchAction = 'none';
43826         canvas.dom.style.msTouchAction = 'none';
43827         
43828         this.mouse_btn_down = false;
43829         canvas.on('mousedown', this._handleMouseDown, this);
43830         canvas.on('mousemove', this._handleMouseMove, this);
43831         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43832         
43833         if (window.PointerEvent) {
43834             canvas.on('pointerdown', this._handleMouseDown, this);
43835             canvas.on('pointermove', this._handleMouseMove, this);
43836             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43837         }
43838         
43839         if ('ontouchstart' in window) {
43840             canvas.on('touchstart', this._handleTouchStart, this);
43841             canvas.on('touchmove', this._handleTouchMove, this);
43842             canvas.on('touchend', this._handleTouchEnd, this);
43843         }
43844         
43845         Roo.EventManager.onWindowResize(this.resize, this, true);
43846         
43847         // file input event
43848         this.fileEl().on('change', this.uploadImage, this);
43849         
43850         this.clear();
43851         
43852         this.resize();
43853     },
43854     
43855     resize: function(){
43856         
43857         var canvas = this.canvasEl().dom;
43858         var ctx = this.canvasElCtx();
43859         var img_data = false;
43860         
43861         if(canvas.width > 0) {
43862             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43863         }
43864         // setting canvas width will clean img data
43865         canvas.width = 0;
43866         
43867         var style = window.getComputedStyle ? 
43868             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43869             
43870         var padding_left = parseInt(style.paddingLeft) || 0;
43871         var padding_right = parseInt(style.paddingRight) || 0;
43872         
43873         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43874         
43875         if(img_data) {
43876             ctx.putImageData(img_data, 0, 0);
43877         }
43878     },
43879     
43880     _handleMouseDown: function(e)
43881     {
43882         if (e.browserEvent.which === 1) {
43883             this.mouse_btn_down = true;
43884             this.strokeBegin(e);
43885         }
43886     },
43887     
43888     _handleMouseMove: function (e)
43889     {
43890         if (this.mouse_btn_down) {
43891             this.strokeMoveUpdate(e);
43892         }
43893     },
43894     
43895     _handleMouseUp: function (e)
43896     {
43897         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43898             this.mouse_btn_down = false;
43899             this.strokeEnd(e);
43900         }
43901     },
43902     
43903     _handleTouchStart: function (e) {
43904         
43905         e.preventDefault();
43906         if (e.browserEvent.targetTouches.length === 1) {
43907             // var touch = e.browserEvent.changedTouches[0];
43908             // this.strokeBegin(touch);
43909             
43910              this.strokeBegin(e); // assume e catching the correct xy...
43911         }
43912     },
43913     
43914     _handleTouchMove: function (e) {
43915         e.preventDefault();
43916         // var touch = event.targetTouches[0];
43917         // _this._strokeMoveUpdate(touch);
43918         this.strokeMoveUpdate(e);
43919     },
43920     
43921     _handleTouchEnd: function (e) {
43922         var wasCanvasTouched = e.target === this.canvasEl().dom;
43923         if (wasCanvasTouched) {
43924             e.preventDefault();
43925             // var touch = event.changedTouches[0];
43926             // _this._strokeEnd(touch);
43927             this.strokeEnd(e);
43928         }
43929     },
43930     
43931     reset: function () {
43932         this._lastPoints = [];
43933         this._lastVelocity = 0;
43934         this._lastWidth = (this.min_width + this.max_width) / 2;
43935         this.canvasElCtx().fillStyle = this.dot_color;
43936     },
43937     
43938     strokeMoveUpdate: function(e)
43939     {
43940         this.strokeUpdate(e);
43941         
43942         if (this.throttle) {
43943             this.throttleStroke(this.strokeUpdate, this.throttle);
43944         }
43945         else {
43946             this.strokeUpdate(e);
43947         }
43948     },
43949     
43950     strokeBegin: function(e)
43951     {
43952         var newPointGroup = {
43953             color: this.dot_color,
43954             points: []
43955         };
43956         
43957         if (typeof this.onBegin === 'function') {
43958             this.onBegin(e);
43959         }
43960         
43961         this.curve_data.push(newPointGroup);
43962         this.reset();
43963         this.strokeUpdate(e);
43964     },
43965     
43966     strokeUpdate: function(e)
43967     {
43968         var rect = this.canvasEl().dom.getBoundingClientRect();
43969         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43970         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43971         var lastPoints = lastPointGroup.points;
43972         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43973         var isLastPointTooClose = lastPoint
43974             ? point.distanceTo(lastPoint) <= this.min_distance
43975             : false;
43976         var color = lastPointGroup.color;
43977         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43978             var curve = this.addPoint(point);
43979             if (!lastPoint) {
43980                 this.drawDot({color: color, point: point});
43981             }
43982             else if (curve) {
43983                 this.drawCurve({color: color, curve: curve});
43984             }
43985             lastPoints.push({
43986                 time: point.time,
43987                 x: point.x,
43988                 y: point.y
43989             });
43990         }
43991     },
43992     
43993     strokeEnd: function(e)
43994     {
43995         this.strokeUpdate(e);
43996         if (typeof this.onEnd === 'function') {
43997             this.onEnd(e);
43998         }
43999     },
44000     
44001     addPoint:  function (point) {
44002         var _lastPoints = this._lastPoints;
44003         _lastPoints.push(point);
44004         if (_lastPoints.length > 2) {
44005             if (_lastPoints.length === 3) {
44006                 _lastPoints.unshift(_lastPoints[0]);
44007             }
44008             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44009             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44010             _lastPoints.shift();
44011             return curve;
44012         }
44013         return null;
44014     },
44015     
44016     calculateCurveWidths: function (startPoint, endPoint) {
44017         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44018             (1 - this.velocity_filter_weight) * this._lastVelocity;
44019
44020         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44021         var widths = {
44022             end: newWidth,
44023             start: this._lastWidth
44024         };
44025         
44026         this._lastVelocity = velocity;
44027         this._lastWidth = newWidth;
44028         return widths;
44029     },
44030     
44031     drawDot: function (_a) {
44032         var color = _a.color, point = _a.point;
44033         var ctx = this.canvasElCtx();
44034         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44035         ctx.beginPath();
44036         this.drawCurveSegment(point.x, point.y, width);
44037         ctx.closePath();
44038         ctx.fillStyle = color;
44039         ctx.fill();
44040     },
44041     
44042     drawCurve: function (_a) {
44043         var color = _a.color, curve = _a.curve;
44044         var ctx = this.canvasElCtx();
44045         var widthDelta = curve.endWidth - curve.startWidth;
44046         var drawSteps = Math.floor(curve.length()) * 2;
44047         ctx.beginPath();
44048         ctx.fillStyle = color;
44049         for (var i = 0; i < drawSteps; i += 1) {
44050         var t = i / drawSteps;
44051         var tt = t * t;
44052         var ttt = tt * t;
44053         var u = 1 - t;
44054         var uu = u * u;
44055         var uuu = uu * u;
44056         var x = uuu * curve.startPoint.x;
44057         x += 3 * uu * t * curve.control1.x;
44058         x += 3 * u * tt * curve.control2.x;
44059         x += ttt * curve.endPoint.x;
44060         var y = uuu * curve.startPoint.y;
44061         y += 3 * uu * t * curve.control1.y;
44062         y += 3 * u * tt * curve.control2.y;
44063         y += ttt * curve.endPoint.y;
44064         var width = curve.startWidth + ttt * widthDelta;
44065         this.drawCurveSegment(x, y, width);
44066         }
44067         ctx.closePath();
44068         ctx.fill();
44069     },
44070     
44071     drawCurveSegment: function (x, y, width) {
44072         var ctx = this.canvasElCtx();
44073         ctx.moveTo(x, y);
44074         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44075         this.is_empty = false;
44076     },
44077     
44078     clear: function()
44079     {
44080         var ctx = this.canvasElCtx();
44081         var canvas = this.canvasEl().dom;
44082         ctx.fillStyle = this.bg_color;
44083         ctx.clearRect(0, 0, canvas.width, canvas.height);
44084         ctx.fillRect(0, 0, canvas.width, canvas.height);
44085         this.curve_data = [];
44086         this.reset();
44087         this.is_empty = true;
44088     },
44089     
44090     fileEl: function()
44091     {
44092         return  this.el.select('input',true).first();
44093     },
44094     
44095     canvasEl: function()
44096     {
44097         return this.el.select('canvas',true).first();
44098     },
44099     
44100     canvasElCtx: function()
44101     {
44102         return this.el.select('canvas',true).first().dom.getContext('2d');
44103     },
44104     
44105     getImage: function(type)
44106     {
44107         if(this.is_empty) {
44108             return false;
44109         }
44110         
44111         // encryption ?
44112         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44113     },
44114     
44115     drawFromImage: function(img_src)
44116     {
44117         var img = new Image();
44118         
44119         img.onload = function(){
44120             this.canvasElCtx().drawImage(img, 0, 0);
44121         }.bind(this);
44122         
44123         img.src = img_src;
44124         
44125         this.is_empty = false;
44126     },
44127     
44128     selectImage: function()
44129     {
44130         this.fileEl().dom.click();
44131     },
44132     
44133     uploadImage: function(e)
44134     {
44135         var reader = new FileReader();
44136         
44137         reader.onload = function(e){
44138             var img = new Image();
44139             img.onload = function(){
44140                 this.reset();
44141                 this.canvasElCtx().drawImage(img, 0, 0);
44142             }.bind(this);
44143             img.src = e.target.result;
44144         }.bind(this);
44145         
44146         reader.readAsDataURL(e.target.files[0]);
44147     },
44148     
44149     // Bezier Point Constructor
44150     Point: (function () {
44151         function Point(x, y, time) {
44152             this.x = x;
44153             this.y = y;
44154             this.time = time || Date.now();
44155         }
44156         Point.prototype.distanceTo = function (start) {
44157             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44158         };
44159         Point.prototype.equals = function (other) {
44160             return this.x === other.x && this.y === other.y && this.time === other.time;
44161         };
44162         Point.prototype.velocityFrom = function (start) {
44163             return this.time !== start.time
44164             ? this.distanceTo(start) / (this.time - start.time)
44165             : 0;
44166         };
44167         return Point;
44168     }()),
44169     
44170     
44171     // Bezier Constructor
44172     Bezier: (function () {
44173         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44174             this.startPoint = startPoint;
44175             this.control2 = control2;
44176             this.control1 = control1;
44177             this.endPoint = endPoint;
44178             this.startWidth = startWidth;
44179             this.endWidth = endWidth;
44180         }
44181         Bezier.fromPoints = function (points, widths, scope) {
44182             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44183             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44184             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44185         };
44186         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44187             var dx1 = s1.x - s2.x;
44188             var dy1 = s1.y - s2.y;
44189             var dx2 = s2.x - s3.x;
44190             var dy2 = s2.y - s3.y;
44191             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44192             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44193             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44194             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44195             var dxm = m1.x - m2.x;
44196             var dym = m1.y - m2.y;
44197             var k = l2 / (l1 + l2);
44198             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44199             var tx = s2.x - cm.x;
44200             var ty = s2.y - cm.y;
44201             return {
44202                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44203                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44204             };
44205         };
44206         Bezier.prototype.length = function () {
44207             var steps = 10;
44208             var length = 0;
44209             var px;
44210             var py;
44211             for (var i = 0; i <= steps; i += 1) {
44212                 var t = i / steps;
44213                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44214                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44215                 if (i > 0) {
44216                     var xdiff = cx - px;
44217                     var ydiff = cy - py;
44218                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44219                 }
44220                 px = cx;
44221                 py = cy;
44222             }
44223             return length;
44224         };
44225         Bezier.prototype.point = function (t, start, c1, c2, end) {
44226             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44227             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44228             + (3.0 * c2 * (1.0 - t) * t * t)
44229             + (end * t * t * t);
44230         };
44231         return Bezier;
44232     }()),
44233     
44234     throttleStroke: function(fn, wait) {
44235       if (wait === void 0) { wait = 250; }
44236       var previous = 0;
44237       var timeout = null;
44238       var result;
44239       var storedContext;
44240       var storedArgs;
44241       var later = function () {
44242           previous = Date.now();
44243           timeout = null;
44244           result = fn.apply(storedContext, storedArgs);
44245           if (!timeout) {
44246               storedContext = null;
44247               storedArgs = [];
44248           }
44249       };
44250       return function wrapper() {
44251           var args = [];
44252           for (var _i = 0; _i < arguments.length; _i++) {
44253               args[_i] = arguments[_i];
44254           }
44255           var now = Date.now();
44256           var remaining = wait - (now - previous);
44257           storedContext = this;
44258           storedArgs = args;
44259           if (remaining <= 0 || remaining > wait) {
44260               if (timeout) {
44261                   clearTimeout(timeout);
44262                   timeout = null;
44263               }
44264               previous = now;
44265               result = fn.apply(storedContext, storedArgs);
44266               if (!timeout) {
44267                   storedContext = null;
44268                   storedArgs = [];
44269               }
44270           }
44271           else if (!timeout) {
44272               timeout = window.setTimeout(later, remaining);
44273           }
44274           return result;
44275       };
44276   }
44277   
44278 });
44279
44280  
44281
44282