roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @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|light|dark) default none
6030  * @cfg {String} html content of button
6031  * @cfg {String} badge text inside badge
6032  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6033  * @cfg {String} glyphicon DEPRICATED - use fa
6034  * @cfg {String} icon DEPRICATED - use fa
6035  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6036  * @cfg {Boolean} active Is item active
6037  * @cfg {Boolean} disabled Is item disabled
6038  
6039  * @cfg {Boolean} preventDefault (true | false) default false
6040  * @cfg {String} tabId the tab that this item activates.
6041  * @cfg {String} tagtype (a|span) render as a href or span?
6042  * @cfg {Boolean} animateRef (true|false) link to element default false  
6043   
6044  * @constructor
6045  * Create a new Navbar Item
6046  * @param {Object} config The config object
6047  */
6048 Roo.bootstrap.NavItem = function(config){
6049     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6050     this.addEvents({
6051         // raw events
6052         /**
6053          * @event click
6054          * The raw click event for the entire grid.
6055          * @param {Roo.EventObject} e
6056          */
6057         "click" : true,
6058          /**
6059             * @event changed
6060             * Fires when the active item active state changes
6061             * @param {Roo.bootstrap.NavItem} this
6062             * @param {boolean} state the new state
6063              
6064          */
6065         'changed': true,
6066         /**
6067             * @event scrollto
6068             * Fires when scroll to element
6069             * @param {Roo.bootstrap.NavItem} this
6070             * @param {Object} options
6071             * @param {Roo.EventObject} e
6072              
6073          */
6074         'scrollto': true
6075     });
6076    
6077 };
6078
6079 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6080     
6081     href: false,
6082     html: '',
6083     badge: '',
6084     icon: false,
6085     fa : false,
6086     glyphicon: false,
6087     active: false,
6088     preventDefault : false,
6089     tabId : false,
6090     tagtype : 'a',
6091     tag: 'li',
6092     disabled : false,
6093     animateRef : false,
6094     was_active : false,
6095     button_weight : '',
6096     button_outline : false,
6097     
6098     navLink: false,
6099     
6100     getAutoCreate : function(){
6101          
6102         var cfg = {
6103             tag: this.tag,
6104             cls: 'nav-item'
6105         };
6106         
6107         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6108         
6109         if (this.active) {
6110             cfg.cls +=  ' active' ;
6111         }
6112         if (this.disabled) {
6113             cfg.cls += ' disabled';
6114         }
6115         
6116         // BS4 only?
6117         if (this.button_weight.length) {
6118             cfg.tag = this.href ? 'a' : 'button';
6119             cfg.html = this.html || '';
6120             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6121             if (this.href) {
6122                 cfg.href = this.href;
6123             }
6124             if (this.fa) {
6125                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6126             }
6127             
6128             // menu .. should add dropdown-menu class - so no need for carat..
6129             
6130             if (this.badge !== '') {
6131                  
6132                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6133             }
6134             return cfg;
6135         }
6136         
6137         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6138             cfg.cn = [
6139                 {
6140                     tag: this.tagtype,
6141                     href : this.href || "#",
6142                     html: this.html || ''
6143                 }
6144             ];
6145             if (this.tagtype == 'a') {
6146                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '');
6147         
6148             }
6149             if (this.icon) {
6150                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6151             }
6152             if (this.fa) {
6153                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if(this.glyphicon) {
6156                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6157             }
6158             
6159             if (this.menu) {
6160                 
6161                 cfg.cn[0].html += " <span class='caret'></span>";
6162              
6163             }
6164             
6165             if (this.badge !== '') {
6166                  
6167                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6168             }
6169         }
6170         
6171         
6172         
6173         return cfg;
6174     },
6175     onRender : function(ct, position)
6176     {
6177        // Roo.log("Call onRender: " + this.xtype);
6178         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6179             this.tag = 'div';
6180         }
6181         
6182         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6183         this.navLink = this.el.select('.nav-link',true).first();
6184         return ret;
6185     },
6186       
6187     
6188     initEvents: function() 
6189     {
6190         if (typeof (this.menu) != 'undefined') {
6191             this.menu.parentType = this.xtype;
6192             this.menu.triggerEl = this.el;
6193             this.menu = this.addxtype(Roo.apply({}, this.menu));
6194         }
6195         
6196         this.el.select('a',true).on('click', this.onClick, this);
6197         
6198         if(this.tagtype == 'span'){
6199             this.el.select('span',true).on('click', this.onClick, this);
6200         }
6201        
6202         // at this point parent should be available..
6203         this.parent().register(this);
6204     },
6205     
6206     onClick : function(e)
6207     {
6208         if (e.getTarget('.dropdown-menu-item')) {
6209             // did you click on a menu itemm.... - then don't trigger onclick..
6210             return;
6211         }
6212         
6213         if(
6214                 this.preventDefault || 
6215                 this.href == '#' 
6216         ){
6217             Roo.log("NavItem - prevent Default?");
6218             e.preventDefault();
6219         }
6220         
6221         if (this.disabled) {
6222             return;
6223         }
6224         
6225         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6226         if (tg && tg.transition) {
6227             Roo.log("waiting for the transitionend");
6228             return;
6229         }
6230         
6231         
6232         
6233         //Roo.log("fire event clicked");
6234         if(this.fireEvent('click', this, e) === false){
6235             return;
6236         };
6237         
6238         if(this.tagtype == 'span'){
6239             return;
6240         }
6241         
6242         //Roo.log(this.href);
6243         var ael = this.el.select('a',true).first();
6244         //Roo.log(ael);
6245         
6246         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6247             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6248             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6249                 return; // ignore... - it's a 'hash' to another page.
6250             }
6251             Roo.log("NavItem - prevent Default?");
6252             e.preventDefault();
6253             this.scrollToElement(e);
6254         }
6255         
6256         
6257         var p =  this.parent();
6258    
6259         if (['tabs','pills'].indexOf(p.type)!==-1) {
6260             if (typeof(p.setActiveItem) !== 'undefined') {
6261                 p.setActiveItem(this);
6262             }
6263         }
6264         
6265         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6266         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6267             // remove the collapsed menu expand...
6268             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6269         }
6270     },
6271     
6272     isActive: function () {
6273         return this.active
6274     },
6275     setActive : function(state, fire, is_was_active)
6276     {
6277         if (this.active && !state && this.navId) {
6278             this.was_active = true;
6279             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6280             if (nv) {
6281                 nv.clearWasActive(this);
6282             }
6283             
6284         }
6285         this.active = state;
6286         
6287         if (!state ) {
6288             this.el.removeClass('active');
6289             this.navLink ? this.navLink.removeClass('active') : false;
6290         } else if (!this.el.hasClass('active')) {
6291             
6292             this.el.addClass('active');
6293             if (Roo.bootstrap.version == 4 && this.navLink ) {
6294                 this.navLink.addClass('active');
6295             }
6296             
6297         }
6298         if (fire) {
6299             this.fireEvent('changed', this, state);
6300         }
6301         
6302         // show a panel if it's registered and related..
6303         
6304         if (!this.navId || !this.tabId || !state || is_was_active) {
6305             return;
6306         }
6307         
6308         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6309         if (!tg) {
6310             return;
6311         }
6312         var pan = tg.getPanelByName(this.tabId);
6313         if (!pan) {
6314             return;
6315         }
6316         // if we can not flip to new panel - go back to old nav highlight..
6317         if (false == tg.showPanel(pan)) {
6318             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6319             if (nv) {
6320                 var onav = nv.getWasActive();
6321                 if (onav) {
6322                     onav.setActive(true, false, true);
6323                 }
6324             }
6325             
6326         }
6327         
6328         
6329         
6330     },
6331      // this should not be here...
6332     setDisabled : function(state)
6333     {
6334         this.disabled = state;
6335         if (!state ) {
6336             this.el.removeClass('disabled');
6337         } else if (!this.el.hasClass('disabled')) {
6338             this.el.addClass('disabled');
6339         }
6340         
6341     },
6342     
6343     /**
6344      * Fetch the element to display the tooltip on.
6345      * @return {Roo.Element} defaults to this.el
6346      */
6347     tooltipEl : function()
6348     {
6349         return this.el.select('' + this.tagtype + '', true).first();
6350     },
6351     
6352     scrollToElement : function(e)
6353     {
6354         var c = document.body;
6355         
6356         /*
6357          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6358          */
6359         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6360             c = document.documentElement;
6361         }
6362         
6363         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6364         
6365         if(!target){
6366             return;
6367         }
6368
6369         var o = target.calcOffsetsTo(c);
6370         
6371         var options = {
6372             target : target,
6373             value : o[1]
6374         };
6375         
6376         this.fireEvent('scrollto', this, options, e);
6377         
6378         Roo.get(c).scrollTo('top', options.value, true);
6379         
6380         return;
6381     }
6382 });
6383  
6384
6385  /*
6386  * - LGPL
6387  *
6388  * sidebar item
6389  *
6390  *  li
6391  *    <span> icon </span>
6392  *    <span> text </span>
6393  *    <span>badge </span>
6394  */
6395
6396 /**
6397  * @class Roo.bootstrap.NavSidebarItem
6398  * @extends Roo.bootstrap.NavItem
6399  * Bootstrap Navbar.NavSidebarItem class
6400  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6401  * {Boolean} open is the menu open
6402  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6403  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6404  * {String} buttonSize (sm|md|lg)the extra classes for the button
6405  * {Boolean} showArrow show arrow next to the text (default true)
6406  * @constructor
6407  * Create a new Navbar Button
6408  * @param {Object} config The config object
6409  */
6410 Roo.bootstrap.NavSidebarItem = function(config){
6411     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6412     this.addEvents({
6413         // raw events
6414         /**
6415          * @event click
6416          * The raw click event for the entire grid.
6417          * @param {Roo.EventObject} e
6418          */
6419         "click" : true,
6420          /**
6421             * @event changed
6422             * Fires when the active item active state changes
6423             * @param {Roo.bootstrap.NavSidebarItem} this
6424             * @param {boolean} state the new state
6425              
6426          */
6427         'changed': true
6428     });
6429    
6430 };
6431
6432 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6433     
6434     badgeWeight : 'default',
6435     
6436     open: false,
6437     
6438     buttonView : false,
6439     
6440     buttonWeight : 'default',
6441     
6442     buttonSize : 'md',
6443     
6444     showArrow : true,
6445     
6446     getAutoCreate : function(){
6447         
6448         
6449         var a = {
6450                 tag: 'a',
6451                 href : this.href || '#',
6452                 cls: '',
6453                 html : '',
6454                 cn : []
6455         };
6456         
6457         if(this.buttonView){
6458             a = {
6459                 tag: 'button',
6460                 href : this.href || '#',
6461                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6462                 html : this.html,
6463                 cn : []
6464             };
6465         }
6466         
6467         var cfg = {
6468             tag: 'li',
6469             cls: '',
6470             cn: [ a ]
6471         };
6472         
6473         if (this.active) {
6474             cfg.cls += ' active';
6475         }
6476         
6477         if (this.disabled) {
6478             cfg.cls += ' disabled';
6479         }
6480         if (this.open) {
6481             cfg.cls += ' open x-open';
6482         }
6483         // left icon..
6484         if (this.glyphicon || this.icon) {
6485             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6486             a.cn.push({ tag : 'i', cls : c }) ;
6487         }
6488         
6489         if(!this.buttonView){
6490             var span = {
6491                 tag: 'span',
6492                 html : this.html || ''
6493             };
6494
6495             a.cn.push(span);
6496             
6497         }
6498         
6499         if (this.badge !== '') {
6500             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6501         }
6502         
6503         if (this.menu) {
6504             
6505             if(this.showArrow){
6506                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6507             }
6508             
6509             a.cls += ' dropdown-toggle treeview' ;
6510         }
6511         
6512         return cfg;
6513     },
6514     
6515     initEvents : function()
6516     { 
6517         if (typeof (this.menu) != 'undefined') {
6518             this.menu.parentType = this.xtype;
6519             this.menu.triggerEl = this.el;
6520             this.menu = this.addxtype(Roo.apply({}, this.menu));
6521         }
6522         
6523         this.el.on('click', this.onClick, this);
6524         
6525         if(this.badge !== ''){
6526             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6527         }
6528         
6529     },
6530     
6531     onClick : function(e)
6532     {
6533         if(this.disabled){
6534             e.preventDefault();
6535             return;
6536         }
6537         
6538         if(this.preventDefault){
6539             e.preventDefault();
6540         }
6541         
6542         this.fireEvent('click', this, e);
6543     },
6544     
6545     disable : function()
6546     {
6547         this.setDisabled(true);
6548     },
6549     
6550     enable : function()
6551     {
6552         this.setDisabled(false);
6553     },
6554     
6555     setDisabled : function(state)
6556     {
6557         if(this.disabled == state){
6558             return;
6559         }
6560         
6561         this.disabled = state;
6562         
6563         if (state) {
6564             this.el.addClass('disabled');
6565             return;
6566         }
6567         
6568         this.el.removeClass('disabled');
6569         
6570         return;
6571     },
6572     
6573     setActive : function(state)
6574     {
6575         if(this.active == state){
6576             return;
6577         }
6578         
6579         this.active = state;
6580         
6581         if (state) {
6582             this.el.addClass('active');
6583             return;
6584         }
6585         
6586         this.el.removeClass('active');
6587         
6588         return;
6589     },
6590     
6591     isActive: function () 
6592     {
6593         return this.active;
6594     },
6595     
6596     setBadge : function(str)
6597     {
6598         if(!this.badgeEl){
6599             return;
6600         }
6601         
6602         this.badgeEl.dom.innerHTML = str;
6603     }
6604     
6605    
6606      
6607  
6608 });
6609  
6610
6611  /*
6612  * - LGPL
6613  *
6614  *  Breadcrumb Nav
6615  * 
6616  */
6617 Roo.namespace('Roo.bootstrap.breadcrumb');
6618
6619
6620 /**
6621  * @class Roo.bootstrap.breadcrumb.Nav
6622  * @extends Roo.bootstrap.Component
6623  * Bootstrap Breadcrumb Nav Class
6624  *  
6625  * @children Roo.bootstrap.breadcrumb.Item
6626  * 
6627  * @constructor
6628  * Create a new breadcrumb.Nav
6629  * @param {Object} config The config object
6630  */
6631
6632
6633 Roo.bootstrap.breadcrumb.Nav = function(config){
6634     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6635     
6636     
6637 };
6638
6639 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6640     
6641     getAutoCreate : function()
6642     {
6643
6644         var cfg = {
6645             tag: 'nav',
6646             cn : [
6647                 {
6648                     tag : 'ol',
6649                     cls : 'breadcrumb'
6650                 }
6651             ]
6652             
6653         };
6654           
6655         return cfg;
6656     },
6657     
6658     initEvents: function()
6659     {
6660         this.olEl = this.el.select('ol',true).first();    
6661     },
6662     getChildContainer : function()
6663     {
6664         return this.olEl;  
6665     }
6666     
6667 });
6668
6669  /*
6670  * - LGPL
6671  *
6672  *  Breadcrumb Item
6673  * 
6674  */
6675
6676
6677 /**
6678  * @class Roo.bootstrap.breadcrumb.Nav
6679  * @extends Roo.bootstrap.Component
6680  * Bootstrap Breadcrumb Nav Class
6681  *  
6682  * @children Roo.bootstrap.breadcrumb.Component
6683  * @cfg {String} html the content of the link.
6684  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6685  * @cfg {Boolean} active is it active
6686
6687  * 
6688  * @constructor
6689  * Create a new breadcrumb.Nav
6690  * @param {Object} config The config object
6691  */
6692
6693 Roo.bootstrap.breadcrumb.Item = function(config){
6694     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6695     this.addEvents({
6696         // img events
6697         /**
6698          * @event click
6699          * The img click event for the img.
6700          * @param {Roo.EventObject} e
6701          */
6702         "click" : true
6703     });
6704     
6705 };
6706
6707 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6708     
6709     href: false,
6710     html : '',
6711     
6712     getAutoCreate : function()
6713     {
6714
6715         var cfg = {
6716             tag: 'li',
6717             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6718         };
6719         if (this.href !== false) {
6720             cfg.cn = [{
6721                 tag : 'a',
6722                 href : this.href,
6723                 html : this.html
6724             }];
6725         } else {
6726             cfg.html = this.html;
6727         }
6728         
6729         return cfg;
6730     },
6731     
6732     initEvents: function()
6733     {
6734         if (this.href) {
6735             this.el.select('a', true).first().on('click',this.onClick, this)
6736         }
6737         
6738     },
6739     onClick : function(e)
6740     {
6741         e.preventDefault();
6742         this.fireEvent('click',this,  e);
6743     }
6744     
6745 });
6746
6747  /*
6748  * - LGPL
6749  *
6750  * row
6751  * 
6752  */
6753
6754 /**
6755  * @class Roo.bootstrap.Row
6756  * @extends Roo.bootstrap.Component
6757  * Bootstrap Row class (contains columns...)
6758  * 
6759  * @constructor
6760  * Create a new Row
6761  * @param {Object} config The config object
6762  */
6763
6764 Roo.bootstrap.Row = function(config){
6765     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6766 };
6767
6768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6769     
6770     getAutoCreate : function(){
6771        return {
6772             cls: 'row clearfix'
6773        };
6774     }
6775     
6776     
6777 });
6778
6779  
6780
6781  /*
6782  * - LGPL
6783  *
6784  * pagination
6785  * 
6786  */
6787
6788 /**
6789  * @class Roo.bootstrap.Pagination
6790  * @extends Roo.bootstrap.Component
6791  * Bootstrap Pagination class
6792  * @cfg {String} size xs | sm | md | lg
6793  * @cfg {Boolean} inverse false | true
6794  * 
6795  * @constructor
6796  * Create a new Pagination
6797  * @param {Object} config The config object
6798  */
6799
6800 Roo.bootstrap.Pagination = function(config){
6801     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6802 };
6803
6804 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6805     
6806     cls: false,
6807     size: false,
6808     inverse: false,
6809     
6810     getAutoCreate : function(){
6811         var cfg = {
6812             tag: 'ul',
6813                 cls: 'pagination'
6814         };
6815         if (this.inverse) {
6816             cfg.cls += ' inverse';
6817         }
6818         if (this.html) {
6819             cfg.html=this.html;
6820         }
6821         if (this.cls) {
6822             cfg.cls += " " + this.cls;
6823         }
6824         return cfg;
6825     }
6826    
6827 });
6828
6829  
6830
6831  /*
6832  * - LGPL
6833  *
6834  * Pagination item
6835  * 
6836  */
6837
6838
6839 /**
6840  * @class Roo.bootstrap.PaginationItem
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap PaginationItem class
6843  * @cfg {String} html text
6844  * @cfg {String} href the link
6845  * @cfg {Boolean} preventDefault (true | false) default true
6846  * @cfg {Boolean} active (true | false) default false
6847  * @cfg {Boolean} disabled default false
6848  * 
6849  * 
6850  * @constructor
6851  * Create a new PaginationItem
6852  * @param {Object} config The config object
6853  */
6854
6855
6856 Roo.bootstrap.PaginationItem = function(config){
6857     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6858     this.addEvents({
6859         // raw events
6860         /**
6861          * @event click
6862          * The raw click event for the entire grid.
6863          * @param {Roo.EventObject} e
6864          */
6865         "click" : true
6866     });
6867 };
6868
6869 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6870     
6871     href : false,
6872     html : false,
6873     preventDefault: true,
6874     active : false,
6875     cls : false,
6876     disabled: false,
6877     
6878     getAutoCreate : function(){
6879         var cfg= {
6880             tag: 'li',
6881             cn: [
6882                 {
6883                     tag : 'a',
6884                     href : this.href ? this.href : '#',
6885                     html : this.html ? this.html : ''
6886                 }
6887             ]
6888         };
6889         
6890         if(this.cls){
6891             cfg.cls = this.cls;
6892         }
6893         
6894         if(this.disabled){
6895             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6896         }
6897         
6898         if(this.active){
6899             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6900         }
6901         
6902         return cfg;
6903     },
6904     
6905     initEvents: function() {
6906         
6907         this.el.on('click', this.onClick, this);
6908         
6909     },
6910     onClick : function(e)
6911     {
6912         Roo.log('PaginationItem on click ');
6913         if(this.preventDefault){
6914             e.preventDefault();
6915         }
6916         
6917         if(this.disabled){
6918             return;
6919         }
6920         
6921         this.fireEvent('click', this, e);
6922     }
6923    
6924 });
6925
6926  
6927
6928  /*
6929  * - LGPL
6930  *
6931  * slider
6932  * 
6933  */
6934
6935
6936 /**
6937  * @class Roo.bootstrap.Slider
6938  * @extends Roo.bootstrap.Component
6939  * Bootstrap Slider class
6940  *    
6941  * @constructor
6942  * Create a new Slider
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.Slider = function(config){
6947     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6948 };
6949
6950 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6951     
6952     getAutoCreate : function(){
6953         
6954         var cfg = {
6955             tag: 'div',
6956             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6957             cn: [
6958                 {
6959                     tag: 'a',
6960                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6961                 }
6962             ]
6963         };
6964         
6965         return cfg;
6966     }
6967    
6968 });
6969
6970  /*
6971  * Based on:
6972  * Ext JS Library 1.1.1
6973  * Copyright(c) 2006-2007, Ext JS, LLC.
6974  *
6975  * Originally Released Under LGPL - original licence link has changed is not relivant.
6976  *
6977  * Fork - LGPL
6978  * <script type="text/javascript">
6979  */
6980  
6981
6982 /**
6983  * @class Roo.grid.ColumnModel
6984  * @extends Roo.util.Observable
6985  * This is the default implementation of a ColumnModel used by the Grid. It defines
6986  * the columns in the grid.
6987  * <br>Usage:<br>
6988  <pre><code>
6989  var colModel = new Roo.grid.ColumnModel([
6990         {header: "Ticker", width: 60, sortable: true, locked: true},
6991         {header: "Company Name", width: 150, sortable: true},
6992         {header: "Market Cap.", width: 100, sortable: true},
6993         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6994         {header: "Employees", width: 100, sortable: true, resizable: false}
6995  ]);
6996  </code></pre>
6997  * <p>
6998  
6999  * The config options listed for this class are options which may appear in each
7000  * individual column definition.
7001  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7002  * @constructor
7003  * @param {Object} config An Array of column config objects. See this class's
7004  * config objects for details.
7005 */
7006 Roo.grid.ColumnModel = function(config){
7007         /**
7008      * The config passed into the constructor
7009      */
7010     this.config = config;
7011     this.lookup = {};
7012
7013     // if no id, create one
7014     // if the column does not have a dataIndex mapping,
7015     // map it to the order it is in the config
7016     for(var i = 0, len = config.length; i < len; i++){
7017         var c = config[i];
7018         if(typeof c.dataIndex == "undefined"){
7019             c.dataIndex = i;
7020         }
7021         if(typeof c.renderer == "string"){
7022             c.renderer = Roo.util.Format[c.renderer];
7023         }
7024         if(typeof c.id == "undefined"){
7025             c.id = Roo.id();
7026         }
7027         if(c.editor && c.editor.xtype){
7028             c.editor  = Roo.factory(c.editor, Roo.grid);
7029         }
7030         if(c.editor && c.editor.isFormField){
7031             c.editor = new Roo.grid.GridEditor(c.editor);
7032         }
7033         this.lookup[c.id] = c;
7034     }
7035
7036     /**
7037      * The width of columns which have no width specified (defaults to 100)
7038      * @type Number
7039      */
7040     this.defaultWidth = 100;
7041
7042     /**
7043      * Default sortable of columns which have no sortable specified (defaults to false)
7044      * @type Boolean
7045      */
7046     this.defaultSortable = false;
7047
7048     this.addEvents({
7049         /**
7050              * @event widthchange
7051              * Fires when the width of a column changes.
7052              * @param {ColumnModel} this
7053              * @param {Number} columnIndex The column index
7054              * @param {Number} newWidth The new width
7055              */
7056             "widthchange": true,
7057         /**
7058              * @event headerchange
7059              * Fires when the text of a header changes.
7060              * @param {ColumnModel} this
7061              * @param {Number} columnIndex The column index
7062              * @param {Number} newText The new header text
7063              */
7064             "headerchange": true,
7065         /**
7066              * @event hiddenchange
7067              * Fires when a column is hidden or "unhidden".
7068              * @param {ColumnModel} this
7069              * @param {Number} columnIndex The column index
7070              * @param {Boolean} hidden true if hidden, false otherwise
7071              */
7072             "hiddenchange": true,
7073             /**
7074          * @event columnmoved
7075          * Fires when a column is moved.
7076          * @param {ColumnModel} this
7077          * @param {Number} oldIndex
7078          * @param {Number} newIndex
7079          */
7080         "columnmoved" : true,
7081         /**
7082          * @event columlockchange
7083          * Fires when a column's locked state is changed
7084          * @param {ColumnModel} this
7085          * @param {Number} colIndex
7086          * @param {Boolean} locked true if locked
7087          */
7088         "columnlockchange" : true
7089     });
7090     Roo.grid.ColumnModel.superclass.constructor.call(this);
7091 };
7092 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7093     /**
7094      * @cfg {String} header The header text to display in the Grid view.
7095      */
7096     /**
7097      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7098      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7099      * specified, the column's index is used as an index into the Record's data Array.
7100      */
7101     /**
7102      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7103      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7104      */
7105     /**
7106      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7107      * Defaults to the value of the {@link #defaultSortable} property.
7108      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7109      */
7110     /**
7111      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7112      */
7113     /**
7114      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7118      */
7119     /**
7120      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7121      */
7122     /**
7123      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7124      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7125      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7126      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7127      */
7128        /**
7129      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7130      */
7131     /**
7132      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7133      */
7134     /**
7135      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} cursor (Optional)
7139      */
7140     /**
7141      * @cfg {String} tooltip (Optional)
7142      */
7143     /**
7144      * @cfg {Number} xs (Optional)
7145      */
7146     /**
7147      * @cfg {Number} sm (Optional)
7148      */
7149     /**
7150      * @cfg {Number} md (Optional)
7151      */
7152     /**
7153      * @cfg {Number} lg (Optional)
7154      */
7155     /**
7156      * Returns the id of the column at the specified index.
7157      * @param {Number} index The column index
7158      * @return {String} the id
7159      */
7160     getColumnId : function(index){
7161         return this.config[index].id;
7162     },
7163
7164     /**
7165      * Returns the column for a specified id.
7166      * @param {String} id The column id
7167      * @return {Object} the column
7168      */
7169     getColumnById : function(id){
7170         return this.lookup[id];
7171     },
7172
7173     
7174     /**
7175      * Returns the column for a specified dataIndex.
7176      * @param {String} dataIndex The column dataIndex
7177      * @return {Object|Boolean} the column or false if not found
7178      */
7179     getColumnByDataIndex: function(dataIndex){
7180         var index = this.findColumnIndex(dataIndex);
7181         return index > -1 ? this.config[index] : false;
7182     },
7183     
7184     /**
7185      * Returns the index for a specified column id.
7186      * @param {String} id The column id
7187      * @return {Number} the index, or -1 if not found
7188      */
7189     getIndexById : function(id){
7190         for(var i = 0, len = this.config.length; i < len; i++){
7191             if(this.config[i].id == id){
7192                 return i;
7193             }
7194         }
7195         return -1;
7196     },
7197     
7198     /**
7199      * Returns the index for a specified column dataIndex.
7200      * @param {String} dataIndex The column dataIndex
7201      * @return {Number} the index, or -1 if not found
7202      */
7203     
7204     findColumnIndex : function(dataIndex){
7205         for(var i = 0, len = this.config.length; i < len; i++){
7206             if(this.config[i].dataIndex == dataIndex){
7207                 return i;
7208             }
7209         }
7210         return -1;
7211     },
7212     
7213     
7214     moveColumn : function(oldIndex, newIndex){
7215         var c = this.config[oldIndex];
7216         this.config.splice(oldIndex, 1);
7217         this.config.splice(newIndex, 0, c);
7218         this.dataMap = null;
7219         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7220     },
7221
7222     isLocked : function(colIndex){
7223         return this.config[colIndex].locked === true;
7224     },
7225
7226     setLocked : function(colIndex, value, suppressEvent){
7227         if(this.isLocked(colIndex) == value){
7228             return;
7229         }
7230         this.config[colIndex].locked = value;
7231         if(!suppressEvent){
7232             this.fireEvent("columnlockchange", this, colIndex, value);
7233         }
7234     },
7235
7236     getTotalLockedWidth : function(){
7237         var totalWidth = 0;
7238         for(var i = 0; i < this.config.length; i++){
7239             if(this.isLocked(i) && !this.isHidden(i)){
7240                 this.totalWidth += this.getColumnWidth(i);
7241             }
7242         }
7243         return totalWidth;
7244     },
7245
7246     getLockedCount : function(){
7247         for(var i = 0, len = this.config.length; i < len; i++){
7248             if(!this.isLocked(i)){
7249                 return i;
7250             }
7251         }
7252         
7253         return this.config.length;
7254     },
7255
7256     /**
7257      * Returns the number of columns.
7258      * @return {Number}
7259      */
7260     getColumnCount : function(visibleOnly){
7261         if(visibleOnly === true){
7262             var c = 0;
7263             for(var i = 0, len = this.config.length; i < len; i++){
7264                 if(!this.isHidden(i)){
7265                     c++;
7266                 }
7267             }
7268             return c;
7269         }
7270         return this.config.length;
7271     },
7272
7273     /**
7274      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7275      * @param {Function} fn
7276      * @param {Object} scope (optional)
7277      * @return {Array} result
7278      */
7279     getColumnsBy : function(fn, scope){
7280         var r = [];
7281         for(var i = 0, len = this.config.length; i < len; i++){
7282             var c = this.config[i];
7283             if(fn.call(scope||this, c, i) === true){
7284                 r[r.length] = c;
7285             }
7286         }
7287         return r;
7288     },
7289
7290     /**
7291      * Returns true if the specified column is sortable.
7292      * @param {Number} col The column index
7293      * @return {Boolean}
7294      */
7295     isSortable : function(col){
7296         if(typeof this.config[col].sortable == "undefined"){
7297             return this.defaultSortable;
7298         }
7299         return this.config[col].sortable;
7300     },
7301
7302     /**
7303      * Returns the rendering (formatting) function defined for the column.
7304      * @param {Number} col The column index.
7305      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7306      */
7307     getRenderer : function(col){
7308         if(!this.config[col].renderer){
7309             return Roo.grid.ColumnModel.defaultRenderer;
7310         }
7311         return this.config[col].renderer;
7312     },
7313
7314     /**
7315      * Sets the rendering (formatting) function for a column.
7316      * @param {Number} col The column index
7317      * @param {Function} fn The function to use to process the cell's raw data
7318      * to return HTML markup for the grid view. The render function is called with
7319      * the following parameters:<ul>
7320      * <li>Data value.</li>
7321      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7322      * <li>css A CSS style string to apply to the table cell.</li>
7323      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7324      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7325      * <li>Row index</li>
7326      * <li>Column index</li>
7327      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7328      */
7329     setRenderer : function(col, fn){
7330         this.config[col].renderer = fn;
7331     },
7332
7333     /**
7334      * Returns the width for the specified column.
7335      * @param {Number} col The column index
7336      * @return {Number}
7337      */
7338     getColumnWidth : function(col){
7339         return this.config[col].width * 1 || this.defaultWidth;
7340     },
7341
7342     /**
7343      * Sets the width for a column.
7344      * @param {Number} col The column index
7345      * @param {Number} width The new width
7346      */
7347     setColumnWidth : function(col, width, suppressEvent){
7348         this.config[col].width = width;
7349         this.totalWidth = null;
7350         if(!suppressEvent){
7351              this.fireEvent("widthchange", this, col, width);
7352         }
7353     },
7354
7355     /**
7356      * Returns the total width of all columns.
7357      * @param {Boolean} includeHidden True to include hidden column widths
7358      * @return {Number}
7359      */
7360     getTotalWidth : function(includeHidden){
7361         if(!this.totalWidth){
7362             this.totalWidth = 0;
7363             for(var i = 0, len = this.config.length; i < len; i++){
7364                 if(includeHidden || !this.isHidden(i)){
7365                     this.totalWidth += this.getColumnWidth(i);
7366                 }
7367             }
7368         }
7369         return this.totalWidth;
7370     },
7371
7372     /**
7373      * Returns the header for the specified column.
7374      * @param {Number} col The column index
7375      * @return {String}
7376      */
7377     getColumnHeader : function(col){
7378         return this.config[col].header;
7379     },
7380
7381     /**
7382      * Sets the header for a column.
7383      * @param {Number} col The column index
7384      * @param {String} header The new header
7385      */
7386     setColumnHeader : function(col, header){
7387         this.config[col].header = header;
7388         this.fireEvent("headerchange", this, col, header);
7389     },
7390
7391     /**
7392      * Returns the tooltip for the specified column.
7393      * @param {Number} col The column index
7394      * @return {String}
7395      */
7396     getColumnTooltip : function(col){
7397             return this.config[col].tooltip;
7398     },
7399     /**
7400      * Sets the tooltip for a column.
7401      * @param {Number} col The column index
7402      * @param {String} tooltip The new tooltip
7403      */
7404     setColumnTooltip : function(col, tooltip){
7405             this.config[col].tooltip = tooltip;
7406     },
7407
7408     /**
7409      * Returns the dataIndex for the specified column.
7410      * @param {Number} col The column index
7411      * @return {Number}
7412      */
7413     getDataIndex : function(col){
7414         return this.config[col].dataIndex;
7415     },
7416
7417     /**
7418      * Sets the dataIndex for a column.
7419      * @param {Number} col The column index
7420      * @param {Number} dataIndex The new dataIndex
7421      */
7422     setDataIndex : function(col, dataIndex){
7423         this.config[col].dataIndex = dataIndex;
7424     },
7425
7426     
7427     
7428     /**
7429      * Returns true if the cell is editable.
7430      * @param {Number} colIndex The column index
7431      * @param {Number} rowIndex The row index - this is nto actually used..?
7432      * @return {Boolean}
7433      */
7434     isCellEditable : function(colIndex, rowIndex){
7435         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7436     },
7437
7438     /**
7439      * Returns the editor defined for the cell/column.
7440      * return false or null to disable editing.
7441      * @param {Number} colIndex The column index
7442      * @param {Number} rowIndex The row index
7443      * @return {Object}
7444      */
7445     getCellEditor : function(colIndex, rowIndex){
7446         return this.config[colIndex].editor;
7447     },
7448
7449     /**
7450      * Sets if a column is editable.
7451      * @param {Number} col The column index
7452      * @param {Boolean} editable True if the column is editable
7453      */
7454     setEditable : function(col, editable){
7455         this.config[col].editable = editable;
7456     },
7457
7458
7459     /**
7460      * Returns true if the column is hidden.
7461      * @param {Number} colIndex The column index
7462      * @return {Boolean}
7463      */
7464     isHidden : function(colIndex){
7465         return this.config[colIndex].hidden;
7466     },
7467
7468
7469     /**
7470      * Returns true if the column width cannot be changed
7471      */
7472     isFixed : function(colIndex){
7473         return this.config[colIndex].fixed;
7474     },
7475
7476     /**
7477      * Returns true if the column can be resized
7478      * @return {Boolean}
7479      */
7480     isResizable : function(colIndex){
7481         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7482     },
7483     /**
7484      * Sets if a column is hidden.
7485      * @param {Number} colIndex The column index
7486      * @param {Boolean} hidden True if the column is hidden
7487      */
7488     setHidden : function(colIndex, hidden){
7489         this.config[colIndex].hidden = hidden;
7490         this.totalWidth = null;
7491         this.fireEvent("hiddenchange", this, colIndex, hidden);
7492     },
7493
7494     /**
7495      * Sets the editor for a column.
7496      * @param {Number} col The column index
7497      * @param {Object} editor The editor object
7498      */
7499     setEditor : function(col, editor){
7500         this.config[col].editor = editor;
7501     }
7502 });
7503
7504 Roo.grid.ColumnModel.defaultRenderer = function(value)
7505 {
7506     if(typeof value == "object") {
7507         return value;
7508     }
7509         if(typeof value == "string" && value.length < 1){
7510             return "&#160;";
7511         }
7512     
7513         return String.format("{0}", value);
7514 };
7515
7516 // Alias for backwards compatibility
7517 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7518 /*
7519  * Based on:
7520  * Ext JS Library 1.1.1
7521  * Copyright(c) 2006-2007, Ext JS, LLC.
7522  *
7523  * Originally Released Under LGPL - original licence link has changed is not relivant.
7524  *
7525  * Fork - LGPL
7526  * <script type="text/javascript">
7527  */
7528  
7529 /**
7530  * @class Roo.LoadMask
7531  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7532  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7533  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7534  * element's UpdateManager load indicator and will be destroyed after the initial load.
7535  * @constructor
7536  * Create a new LoadMask
7537  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7538  * @param {Object} config The config object
7539  */
7540 Roo.LoadMask = function(el, config){
7541     this.el = Roo.get(el);
7542     Roo.apply(this, config);
7543     if(this.store){
7544         this.store.on('beforeload', this.onBeforeLoad, this);
7545         this.store.on('load', this.onLoad, this);
7546         this.store.on('loadexception', this.onLoadException, this);
7547         this.removeMask = false;
7548     }else{
7549         var um = this.el.getUpdateManager();
7550         um.showLoadIndicator = false; // disable the default indicator
7551         um.on('beforeupdate', this.onBeforeLoad, this);
7552         um.on('update', this.onLoad, this);
7553         um.on('failure', this.onLoad, this);
7554         this.removeMask = true;
7555     }
7556 };
7557
7558 Roo.LoadMask.prototype = {
7559     /**
7560      * @cfg {Boolean} removeMask
7561      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7562      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7563      */
7564     /**
7565      * @cfg {String} msg
7566      * The text to display in a centered loading message box (defaults to 'Loading...')
7567      */
7568     msg : 'Loading...',
7569     /**
7570      * @cfg {String} msgCls
7571      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7572      */
7573     msgCls : 'x-mask-loading',
7574
7575     /**
7576      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7577      * @type Boolean
7578      */
7579     disabled: false,
7580
7581     /**
7582      * Disables the mask to prevent it from being displayed
7583      */
7584     disable : function(){
7585        this.disabled = true;
7586     },
7587
7588     /**
7589      * Enables the mask so that it can be displayed
7590      */
7591     enable : function(){
7592         this.disabled = false;
7593     },
7594     
7595     onLoadException : function()
7596     {
7597         Roo.log(arguments);
7598         
7599         if (typeof(arguments[3]) != 'undefined') {
7600             Roo.MessageBox.alert("Error loading",arguments[3]);
7601         } 
7602         /*
7603         try {
7604             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7605                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7606             }   
7607         } catch(e) {
7608             
7609         }
7610         */
7611     
7612         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7613     },
7614     // private
7615     onLoad : function()
7616     {
7617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7618     },
7619
7620     // private
7621     onBeforeLoad : function(){
7622         if(!this.disabled){
7623             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7624         }
7625     },
7626
7627     // private
7628     destroy : function(){
7629         if(this.store){
7630             this.store.un('beforeload', this.onBeforeLoad, this);
7631             this.store.un('load', this.onLoad, this);
7632             this.store.un('loadexception', this.onLoadException, this);
7633         }else{
7634             var um = this.el.getUpdateManager();
7635             um.un('beforeupdate', this.onBeforeLoad, this);
7636             um.un('update', this.onLoad, this);
7637             um.un('failure', this.onLoad, this);
7638         }
7639     }
7640 };/*
7641  * - LGPL
7642  *
7643  * table
7644  * 
7645  */
7646
7647 /**
7648  * @class Roo.bootstrap.Table
7649  * @extends Roo.bootstrap.Component
7650  * Bootstrap Table class
7651  * @cfg {String} cls table class
7652  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7653  * @cfg {String} bgcolor Specifies the background color for a table
7654  * @cfg {Number} border Specifies whether the table cells should have borders or not
7655  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7656  * @cfg {Number} cellspacing Specifies the space between cells
7657  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7658  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7659  * @cfg {String} sortable Specifies that the table should be sortable
7660  * @cfg {String} summary Specifies a summary of the content of a table
7661  * @cfg {Number} width Specifies the width of a table
7662  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7663  * 
7664  * @cfg {boolean} striped Should the rows be alternative striped
7665  * @cfg {boolean} bordered Add borders to the table
7666  * @cfg {boolean} hover Add hover highlighting
7667  * @cfg {boolean} condensed Format condensed
7668  * @cfg {boolean} responsive Format condensed
7669  * @cfg {Boolean} loadMask (true|false) default false
7670  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7671  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7672  * @cfg {Boolean} rowSelection (true|false) default false
7673  * @cfg {Boolean} cellSelection (true|false) default false
7674  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7675  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7676  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7677  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7678  
7679  * 
7680  * @constructor
7681  * Create a new Table
7682  * @param {Object} config The config object
7683  */
7684
7685 Roo.bootstrap.Table = function(config){
7686     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7687     
7688   
7689     
7690     // BC...
7691     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7692     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7693     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7694     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7695     
7696     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7697     if (this.sm) {
7698         this.sm.grid = this;
7699         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7700         this.sm = this.selModel;
7701         this.sm.xmodule = this.xmodule || false;
7702     }
7703     
7704     if (this.cm && typeof(this.cm.config) == 'undefined') {
7705         this.colModel = new Roo.grid.ColumnModel(this.cm);
7706         this.cm = this.colModel;
7707         this.cm.xmodule = this.xmodule || false;
7708     }
7709     if (this.store) {
7710         this.store= Roo.factory(this.store, Roo.data);
7711         this.ds = this.store;
7712         this.ds.xmodule = this.xmodule || false;
7713          
7714     }
7715     if (this.footer && this.store) {
7716         this.footer.dataSource = this.ds;
7717         this.footer = Roo.factory(this.footer);
7718     }
7719     
7720     /** @private */
7721     this.addEvents({
7722         /**
7723          * @event cellclick
7724          * Fires when a cell is clicked
7725          * @param {Roo.bootstrap.Table} this
7726          * @param {Roo.Element} el
7727          * @param {Number} rowIndex
7728          * @param {Number} columnIndex
7729          * @param {Roo.EventObject} e
7730          */
7731         "cellclick" : true,
7732         /**
7733          * @event celldblclick
7734          * Fires when a cell is double clicked
7735          * @param {Roo.bootstrap.Table} this
7736          * @param {Roo.Element} el
7737          * @param {Number} rowIndex
7738          * @param {Number} columnIndex
7739          * @param {Roo.EventObject} e
7740          */
7741         "celldblclick" : true,
7742         /**
7743          * @event rowclick
7744          * Fires when a row is clicked
7745          * @param {Roo.bootstrap.Table} this
7746          * @param {Roo.Element} el
7747          * @param {Number} rowIndex
7748          * @param {Roo.EventObject} e
7749          */
7750         "rowclick" : true,
7751         /**
7752          * @event rowdblclick
7753          * Fires when a row is double clicked
7754          * @param {Roo.bootstrap.Table} this
7755          * @param {Roo.Element} el
7756          * @param {Number} rowIndex
7757          * @param {Roo.EventObject} e
7758          */
7759         "rowdblclick" : true,
7760         /**
7761          * @event mouseover
7762          * Fires when a mouseover occur
7763          * @param {Roo.bootstrap.Table} this
7764          * @param {Roo.Element} el
7765          * @param {Number} rowIndex
7766          * @param {Number} columnIndex
7767          * @param {Roo.EventObject} e
7768          */
7769         "mouseover" : true,
7770         /**
7771          * @event mouseout
7772          * Fires when a mouseout occur
7773          * @param {Roo.bootstrap.Table} this
7774          * @param {Roo.Element} el
7775          * @param {Number} rowIndex
7776          * @param {Number} columnIndex
7777          * @param {Roo.EventObject} e
7778          */
7779         "mouseout" : true,
7780         /**
7781          * @event rowclass
7782          * Fires when a row is rendered, so you can change add a style to it.
7783          * @param {Roo.bootstrap.Table} this
7784          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7785          */
7786         'rowclass' : true,
7787           /**
7788          * @event rowsrendered
7789          * Fires when all the  rows have been rendered
7790          * @param {Roo.bootstrap.Table} this
7791          */
7792         'rowsrendered' : true,
7793         /**
7794          * @event contextmenu
7795          * The raw contextmenu event for the entire grid.
7796          * @param {Roo.EventObject} e
7797          */
7798         "contextmenu" : true,
7799         /**
7800          * @event rowcontextmenu
7801          * Fires when a row is right clicked
7802          * @param {Roo.bootstrap.Table} this
7803          * @param {Number} rowIndex
7804          * @param {Roo.EventObject} e
7805          */
7806         "rowcontextmenu" : true,
7807         /**
7808          * @event cellcontextmenu
7809          * Fires when a cell is right clicked
7810          * @param {Roo.bootstrap.Table} this
7811          * @param {Number} rowIndex
7812          * @param {Number} cellIndex
7813          * @param {Roo.EventObject} e
7814          */
7815          "cellcontextmenu" : true,
7816          /**
7817          * @event headercontextmenu
7818          * Fires when a header is right clicked
7819          * @param {Roo.bootstrap.Table} this
7820          * @param {Number} columnIndex
7821          * @param {Roo.EventObject} e
7822          */
7823         "headercontextmenu" : true
7824     });
7825 };
7826
7827 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7828     
7829     cls: false,
7830     align: false,
7831     bgcolor: false,
7832     border: false,
7833     cellpadding: false,
7834     cellspacing: false,
7835     frame: false,
7836     rules: false,
7837     sortable: false,
7838     summary: false,
7839     width: false,
7840     striped : false,
7841     scrollBody : false,
7842     bordered: false,
7843     hover:  false,
7844     condensed : false,
7845     responsive : false,
7846     sm : false,
7847     cm : false,
7848     store : false,
7849     loadMask : false,
7850     footerShow : true,
7851     headerShow : true,
7852   
7853     rowSelection : false,
7854     cellSelection : false,
7855     layout : false,
7856     
7857     // Roo.Element - the tbody
7858     mainBody: false,
7859     // Roo.Element - thead element
7860     mainHead: false,
7861     
7862     container: false, // used by gridpanel...
7863     
7864     lazyLoad : false,
7865     
7866     CSS : Roo.util.CSS,
7867     
7868     auto_hide_footer : false,
7869     
7870     getAutoCreate : function()
7871     {
7872         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7873         
7874         cfg = {
7875             tag: 'table',
7876             cls : 'table',
7877             cn : []
7878         };
7879         if (this.scrollBody) {
7880             cfg.cls += ' table-body-fixed';
7881         }    
7882         if (this.striped) {
7883             cfg.cls += ' table-striped';
7884         }
7885         
7886         if (this.hover) {
7887             cfg.cls += ' table-hover';
7888         }
7889         if (this.bordered) {
7890             cfg.cls += ' table-bordered';
7891         }
7892         if (this.condensed) {
7893             cfg.cls += ' table-condensed';
7894         }
7895         if (this.responsive) {
7896             cfg.cls += ' table-responsive';
7897         }
7898         
7899         if (this.cls) {
7900             cfg.cls+=  ' ' +this.cls;
7901         }
7902         
7903         // this lot should be simplifed...
7904         var _t = this;
7905         var cp = [
7906             'align',
7907             'bgcolor',
7908             'border',
7909             'cellpadding',
7910             'cellspacing',
7911             'frame',
7912             'rules',
7913             'sortable',
7914             'summary',
7915             'width'
7916         ].forEach(function(k) {
7917             if (_t[k]) {
7918                 cfg[k] = _t[k];
7919             }
7920         });
7921         
7922         
7923         if (this.layout) {
7924             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7925         }
7926         
7927         if(this.store || this.cm){
7928             if(this.headerShow){
7929                 cfg.cn.push(this.renderHeader());
7930             }
7931             
7932             cfg.cn.push(this.renderBody());
7933             
7934             if(this.footerShow){
7935                 cfg.cn.push(this.renderFooter());
7936             }
7937             // where does this come from?
7938             //cfg.cls+=  ' TableGrid';
7939         }
7940         
7941         return { cn : [ cfg ] };
7942     },
7943     
7944     initEvents : function()
7945     {   
7946         if(!this.store || !this.cm){
7947             return;
7948         }
7949         if (this.selModel) {
7950             this.selModel.initEvents();
7951         }
7952         
7953         
7954         //Roo.log('initEvents with ds!!!!');
7955         
7956         this.mainBody = this.el.select('tbody', true).first();
7957         this.mainHead = this.el.select('thead', true).first();
7958         this.mainFoot = this.el.select('tfoot', true).first();
7959         
7960         
7961         
7962         var _this = this;
7963         
7964         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7965             e.on('click', _this.sort, _this);
7966         });
7967         
7968         this.mainBody.on("click", this.onClick, this);
7969         this.mainBody.on("dblclick", this.onDblClick, this);
7970         
7971         // why is this done????? = it breaks dialogs??
7972         //this.parent().el.setStyle('position', 'relative');
7973         
7974         
7975         if (this.footer) {
7976             this.footer.parentId = this.id;
7977             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7978             
7979             if(this.lazyLoad){
7980                 this.el.select('tfoot tr td').first().addClass('hide');
7981             }
7982         } 
7983         
7984         if(this.loadMask) {
7985             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7986         }
7987         
7988         this.store.on('load', this.onLoad, this);
7989         this.store.on('beforeload', this.onBeforeLoad, this);
7990         this.store.on('update', this.onUpdate, this);
7991         this.store.on('add', this.onAdd, this);
7992         this.store.on("clear", this.clear, this);
7993         
7994         this.el.on("contextmenu", this.onContextMenu, this);
7995         
7996         this.mainBody.on('scroll', this.onBodyScroll, this);
7997         
7998         this.cm.on("headerchange", this.onHeaderChange, this);
7999         
8000         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8001         
8002     },
8003     
8004     onContextMenu : function(e, t)
8005     {
8006         this.processEvent("contextmenu", e);
8007     },
8008     
8009     processEvent : function(name, e)
8010     {
8011         if (name != 'touchstart' ) {
8012             this.fireEvent(name, e);    
8013         }
8014         
8015         var t = e.getTarget();
8016         
8017         var cell = Roo.get(t);
8018         
8019         if(!cell){
8020             return;
8021         }
8022         
8023         if(cell.findParent('tfoot', false, true)){
8024             return;
8025         }
8026         
8027         if(cell.findParent('thead', false, true)){
8028             
8029             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8030                 cell = Roo.get(t).findParent('th', false, true);
8031                 if (!cell) {
8032                     Roo.log("failed to find th in thead?");
8033                     Roo.log(e.getTarget());
8034                     return;
8035                 }
8036             }
8037             
8038             var cellIndex = cell.dom.cellIndex;
8039             
8040             var ename = name == 'touchstart' ? 'click' : name;
8041             this.fireEvent("header" + ename, this, cellIndex, e);
8042             
8043             return;
8044         }
8045         
8046         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8047             cell = Roo.get(t).findParent('td', false, true);
8048             if (!cell) {
8049                 Roo.log("failed to find th in tbody?");
8050                 Roo.log(e.getTarget());
8051                 return;
8052             }
8053         }
8054         
8055         var row = cell.findParent('tr', false, true);
8056         var cellIndex = cell.dom.cellIndex;
8057         var rowIndex = row.dom.rowIndex - 1;
8058         
8059         if(row !== false){
8060             
8061             this.fireEvent("row" + name, this, rowIndex, e);
8062             
8063             if(cell !== false){
8064             
8065                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8066             }
8067         }
8068         
8069     },
8070     
8071     onMouseover : function(e, el)
8072     {
8073         var cell = Roo.get(el);
8074         
8075         if(!cell){
8076             return;
8077         }
8078         
8079         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8080             cell = cell.findParent('td', false, true);
8081         }
8082         
8083         var row = cell.findParent('tr', false, true);
8084         var cellIndex = cell.dom.cellIndex;
8085         var rowIndex = row.dom.rowIndex - 1; // start from 0
8086         
8087         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8088         
8089     },
8090     
8091     onMouseout : function(e, el)
8092     {
8093         var cell = Roo.get(el);
8094         
8095         if(!cell){
8096             return;
8097         }
8098         
8099         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8100             cell = cell.findParent('td', false, true);
8101         }
8102         
8103         var row = cell.findParent('tr', false, true);
8104         var cellIndex = cell.dom.cellIndex;
8105         var rowIndex = row.dom.rowIndex - 1; // start from 0
8106         
8107         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8108         
8109     },
8110     
8111     onClick : function(e, el)
8112     {
8113         var cell = Roo.get(el);
8114         
8115         if(!cell || (!this.cellSelection && !this.rowSelection)){
8116             return;
8117         }
8118         
8119         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120             cell = cell.findParent('td', false, true);
8121         }
8122         
8123         if(!cell || typeof(cell) == 'undefined'){
8124             return;
8125         }
8126         
8127         var row = cell.findParent('tr', false, true);
8128         
8129         if(!row || typeof(row) == 'undefined'){
8130             return;
8131         }
8132         
8133         var cellIndex = cell.dom.cellIndex;
8134         var rowIndex = this.getRowIndex(row);
8135         
8136         // why??? - should these not be based on SelectionModel?
8137         if(this.cellSelection){
8138             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8139         }
8140         
8141         if(this.rowSelection){
8142             this.fireEvent('rowclick', this, row, rowIndex, e);
8143         }
8144         
8145         
8146     },
8147         
8148     onDblClick : function(e,el)
8149     {
8150         var cell = Roo.get(el);
8151         
8152         if(!cell || (!this.cellSelection && !this.rowSelection)){
8153             return;
8154         }
8155         
8156         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8157             cell = cell.findParent('td', false, true);
8158         }
8159         
8160         if(!cell || typeof(cell) == 'undefined'){
8161             return;
8162         }
8163         
8164         var row = cell.findParent('tr', false, true);
8165         
8166         if(!row || typeof(row) == 'undefined'){
8167             return;
8168         }
8169         
8170         var cellIndex = cell.dom.cellIndex;
8171         var rowIndex = this.getRowIndex(row);
8172         
8173         if(this.cellSelection){
8174             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8175         }
8176         
8177         if(this.rowSelection){
8178             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8179         }
8180     },
8181     
8182     sort : function(e,el)
8183     {
8184         var col = Roo.get(el);
8185         
8186         if(!col.hasClass('sortable')){
8187             return;
8188         }
8189         
8190         var sort = col.attr('sort');
8191         var dir = 'ASC';
8192         
8193         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8194             dir = 'DESC';
8195         }
8196         
8197         this.store.sortInfo = {field : sort, direction : dir};
8198         
8199         if (this.footer) {
8200             Roo.log("calling footer first");
8201             this.footer.onClick('first');
8202         } else {
8203         
8204             this.store.load({ params : { start : 0 } });
8205         }
8206     },
8207     
8208     renderHeader : function()
8209     {
8210         var header = {
8211             tag: 'thead',
8212             cn : []
8213         };
8214         
8215         var cm = this.cm;
8216         this.totalWidth = 0;
8217         
8218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8219             
8220             var config = cm.config[i];
8221             
8222             var c = {
8223                 tag: 'th',
8224                 cls : 'x-hcol-' + i,
8225                 style : '',
8226                 html: cm.getColumnHeader(i)
8227             };
8228             
8229             var hh = '';
8230             
8231             if(typeof(config.sortable) != 'undefined' && config.sortable){
8232                 c.cls = 'sortable';
8233                 c.html = '<i class="glyphicon"></i>' + c.html;
8234             }
8235             
8236             // could use BS4 hidden-..-down 
8237             
8238             if(typeof(config.lgHeader) != 'undefined'){
8239                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8240             }
8241             
8242             if(typeof(config.mdHeader) != 'undefined'){
8243                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8244             }
8245             
8246             if(typeof(config.smHeader) != 'undefined'){
8247                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8248             }
8249             
8250             if(typeof(config.xsHeader) != 'undefined'){
8251                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8252             }
8253             
8254             if(hh.length){
8255                 c.html = hh;
8256             }
8257             
8258             if(typeof(config.tooltip) != 'undefined'){
8259                 c.tooltip = config.tooltip;
8260             }
8261             
8262             if(typeof(config.colspan) != 'undefined'){
8263                 c.colspan = config.colspan;
8264             }
8265             
8266             if(typeof(config.hidden) != 'undefined' && config.hidden){
8267                 c.style += ' display:none;';
8268             }
8269             
8270             if(typeof(config.dataIndex) != 'undefined'){
8271                 c.sort = config.dataIndex;
8272             }
8273             
8274            
8275             
8276             if(typeof(config.align) != 'undefined' && config.align.length){
8277                 c.style += ' text-align:' + config.align + ';';
8278             }
8279             
8280             if(typeof(config.width) != 'undefined'){
8281                 c.style += ' width:' + config.width + 'px;';
8282                 this.totalWidth += config.width;
8283             } else {
8284                 this.totalWidth += 100; // assume minimum of 100 per column?
8285             }
8286             
8287             if(typeof(config.cls) != 'undefined'){
8288                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8289             }
8290             
8291             ['xs','sm','md','lg'].map(function(size){
8292                 
8293                 if(typeof(config[size]) == 'undefined'){
8294                     return;
8295                 }
8296                  
8297                 if (!config[size]) { // 0 = hidden
8298                     // BS 4 '0' is treated as hide that column and below.
8299                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8300                     return;
8301                 }
8302                 
8303                 c.cls += ' col-' + size + '-' + config[size] + (
8304                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8305                 );
8306                 
8307                 
8308             });
8309             
8310             header.cn.push(c)
8311         }
8312         
8313         return header;
8314     },
8315     
8316     renderBody : function()
8317     {
8318         var body = {
8319             tag: 'tbody',
8320             cn : [
8321                 {
8322                     tag: 'tr',
8323                     cn : [
8324                         {
8325                             tag : 'td',
8326                             colspan :  this.cm.getColumnCount()
8327                         }
8328                     ]
8329                 }
8330             ]
8331         };
8332         
8333         return body;
8334     },
8335     
8336     renderFooter : function()
8337     {
8338         var footer = {
8339             tag: 'tfoot',
8340             cn : [
8341                 {
8342                     tag: 'tr',
8343                     cn : [
8344                         {
8345                             tag : 'td',
8346                             colspan :  this.cm.getColumnCount()
8347                         }
8348                     ]
8349                 }
8350             ]
8351         };
8352         
8353         return footer;
8354     },
8355     
8356     
8357     
8358     onLoad : function()
8359     {
8360 //        Roo.log('ds onload');
8361         this.clear();
8362         
8363         var _this = this;
8364         var cm = this.cm;
8365         var ds = this.store;
8366         
8367         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8368             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8369             if (_this.store.sortInfo) {
8370                     
8371                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8372                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8373                 }
8374                 
8375                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8376                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8377                 }
8378             }
8379         });
8380         
8381         var tbody =  this.mainBody;
8382               
8383         if(ds.getCount() > 0){
8384             ds.data.each(function(d,rowIndex){
8385                 var row =  this.renderRow(cm, ds, rowIndex);
8386                 
8387                 tbody.createChild(row);
8388                 
8389                 var _this = this;
8390                 
8391                 if(row.cellObjects.length){
8392                     Roo.each(row.cellObjects, function(r){
8393                         _this.renderCellObject(r);
8394                     })
8395                 }
8396                 
8397             }, this);
8398         }
8399         
8400         var tfoot = this.el.select('tfoot', true).first();
8401         
8402         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8403             
8404             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8405             
8406             var total = this.ds.getTotalCount();
8407             
8408             if(this.footer.pageSize < total){
8409                 this.mainFoot.show();
8410             }
8411         }
8412         
8413         Roo.each(this.el.select('tbody td', true).elements, function(e){
8414             e.on('mouseover', _this.onMouseover, _this);
8415         });
8416         
8417         Roo.each(this.el.select('tbody td', true).elements, function(e){
8418             e.on('mouseout', _this.onMouseout, _this);
8419         });
8420         this.fireEvent('rowsrendered', this);
8421         
8422         this.autoSize();
8423     },
8424     
8425     
8426     onUpdate : function(ds,record)
8427     {
8428         this.refreshRow(record);
8429         this.autoSize();
8430     },
8431     
8432     onRemove : function(ds, record, index, isUpdate){
8433         if(isUpdate !== true){
8434             this.fireEvent("beforerowremoved", this, index, record);
8435         }
8436         var bt = this.mainBody.dom;
8437         
8438         var rows = this.el.select('tbody > tr', true).elements;
8439         
8440         if(typeof(rows[index]) != 'undefined'){
8441             bt.removeChild(rows[index].dom);
8442         }
8443         
8444 //        if(bt.rows[index]){
8445 //            bt.removeChild(bt.rows[index]);
8446 //        }
8447         
8448         if(isUpdate !== true){
8449             //this.stripeRows(index);
8450             //this.syncRowHeights(index, index);
8451             //this.layout();
8452             this.fireEvent("rowremoved", this, index, record);
8453         }
8454     },
8455     
8456     onAdd : function(ds, records, rowIndex)
8457     {
8458         //Roo.log('on Add called');
8459         // - note this does not handle multiple adding very well..
8460         var bt = this.mainBody.dom;
8461         for (var i =0 ; i < records.length;i++) {
8462             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8463             //Roo.log(records[i]);
8464             //Roo.log(this.store.getAt(rowIndex+i));
8465             this.insertRow(this.store, rowIndex + i, false);
8466             return;
8467         }
8468         
8469     },
8470     
8471     
8472     refreshRow : function(record){
8473         var ds = this.store, index;
8474         if(typeof record == 'number'){
8475             index = record;
8476             record = ds.getAt(index);
8477         }else{
8478             index = ds.indexOf(record);
8479             if (index < 0) {
8480                 return; // should not happen - but seems to 
8481             }
8482         }
8483         this.insertRow(ds, index, true);
8484         this.autoSize();
8485         this.onRemove(ds, record, index+1, true);
8486         this.autoSize();
8487         //this.syncRowHeights(index, index);
8488         //this.layout();
8489         this.fireEvent("rowupdated", this, index, record);
8490     },
8491     
8492     insertRow : function(dm, rowIndex, isUpdate){
8493         
8494         if(!isUpdate){
8495             this.fireEvent("beforerowsinserted", this, rowIndex);
8496         }
8497             //var s = this.getScrollState();
8498         var row = this.renderRow(this.cm, this.store, rowIndex);
8499         // insert before rowIndex..
8500         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8501         
8502         var _this = this;
8503                 
8504         if(row.cellObjects.length){
8505             Roo.each(row.cellObjects, function(r){
8506                 _this.renderCellObject(r);
8507             })
8508         }
8509             
8510         if(!isUpdate){
8511             this.fireEvent("rowsinserted", this, rowIndex);
8512             //this.syncRowHeights(firstRow, lastRow);
8513             //this.stripeRows(firstRow);
8514             //this.layout();
8515         }
8516         
8517     },
8518     
8519     
8520     getRowDom : function(rowIndex)
8521     {
8522         var rows = this.el.select('tbody > tr', true).elements;
8523         
8524         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8525         
8526     },
8527     // returns the object tree for a tr..
8528   
8529     
8530     renderRow : function(cm, ds, rowIndex) 
8531     {
8532         var d = ds.getAt(rowIndex);
8533         
8534         var row = {
8535             tag : 'tr',
8536             cls : 'x-row-' + rowIndex,
8537             cn : []
8538         };
8539             
8540         var cellObjects = [];
8541         
8542         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8543             var config = cm.config[i];
8544             
8545             var renderer = cm.getRenderer(i);
8546             var value = '';
8547             var id = false;
8548             
8549             if(typeof(renderer) !== 'undefined'){
8550                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8551             }
8552             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8553             // and are rendered into the cells after the row is rendered - using the id for the element.
8554             
8555             if(typeof(value) === 'object'){
8556                 id = Roo.id();
8557                 cellObjects.push({
8558                     container : id,
8559                     cfg : value 
8560                 })
8561             }
8562             
8563             var rowcfg = {
8564                 record: d,
8565                 rowIndex : rowIndex,
8566                 colIndex : i,
8567                 rowClass : ''
8568             };
8569
8570             this.fireEvent('rowclass', this, rowcfg);
8571             
8572             var td = {
8573                 tag: 'td',
8574                 cls : rowcfg.rowClass + ' x-col-' + i,
8575                 style: '',
8576                 html: (typeof(value) === 'object') ? '' : value
8577             };
8578             
8579             if (id) {
8580                 td.id = id;
8581             }
8582             
8583             if(typeof(config.colspan) != 'undefined'){
8584                 td.colspan = config.colspan;
8585             }
8586             
8587             if(typeof(config.hidden) != 'undefined' && config.hidden){
8588                 td.style += ' display:none;';
8589             }
8590             
8591             if(typeof(config.align) != 'undefined' && config.align.length){
8592                 td.style += ' text-align:' + config.align + ';';
8593             }
8594             if(typeof(config.valign) != 'undefined' && config.valign.length){
8595                 td.style += ' vertical-align:' + config.valign + ';';
8596             }
8597             
8598             if(typeof(config.width) != 'undefined'){
8599                 td.style += ' width:' +  config.width + 'px;';
8600             }
8601             
8602             if(typeof(config.cursor) != 'undefined'){
8603                 td.style += ' cursor:' +  config.cursor + ';';
8604             }
8605             
8606             if(typeof(config.cls) != 'undefined'){
8607                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8608             }
8609             
8610             ['xs','sm','md','lg'].map(function(size){
8611                 
8612                 if(typeof(config[size]) == 'undefined'){
8613                     return;
8614                 }
8615                 
8616                 
8617                   
8618                 if (!config[size]) { // 0 = hidden
8619                     // BS 4 '0' is treated as hide that column and below.
8620                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8621                     return;
8622                 }
8623                 
8624                 td.cls += ' col-' + size + '-' + config[size] + (
8625                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8626                 );
8627                  
8628
8629             });
8630             
8631             row.cn.push(td);
8632            
8633         }
8634         
8635         row.cellObjects = cellObjects;
8636         
8637         return row;
8638           
8639     },
8640     
8641     
8642     
8643     onBeforeLoad : function()
8644     {
8645         
8646     },
8647      /**
8648      * Remove all rows
8649      */
8650     clear : function()
8651     {
8652         this.el.select('tbody', true).first().dom.innerHTML = '';
8653     },
8654     /**
8655      * Show or hide a row.
8656      * @param {Number} rowIndex to show or hide
8657      * @param {Boolean} state hide
8658      */
8659     setRowVisibility : function(rowIndex, state)
8660     {
8661         var bt = this.mainBody.dom;
8662         
8663         var rows = this.el.select('tbody > tr', true).elements;
8664         
8665         if(typeof(rows[rowIndex]) == 'undefined'){
8666             return;
8667         }
8668         rows[rowIndex].dom.style.display = state ? '' : 'none';
8669     },
8670     
8671     
8672     getSelectionModel : function(){
8673         if(!this.selModel){
8674             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8675         }
8676         return this.selModel;
8677     },
8678     /*
8679      * Render the Roo.bootstrap object from renderder
8680      */
8681     renderCellObject : function(r)
8682     {
8683         var _this = this;
8684         
8685         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8686         
8687         var t = r.cfg.render(r.container);
8688         
8689         if(r.cfg.cn){
8690             Roo.each(r.cfg.cn, function(c){
8691                 var child = {
8692                     container: t.getChildContainer(),
8693                     cfg: c
8694                 };
8695                 _this.renderCellObject(child);
8696             })
8697         }
8698     },
8699     
8700     getRowIndex : function(row)
8701     {
8702         var rowIndex = -1;
8703         
8704         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8705             if(el != row){
8706                 return;
8707             }
8708             
8709             rowIndex = index;
8710         });
8711         
8712         return rowIndex;
8713     },
8714      /**
8715      * Returns the grid's underlying element = used by panel.Grid
8716      * @return {Element} The element
8717      */
8718     getGridEl : function(){
8719         return this.el;
8720     },
8721      /**
8722      * Forces a resize - used by panel.Grid
8723      * @return {Element} The element
8724      */
8725     autoSize : function()
8726     {
8727         //var ctr = Roo.get(this.container.dom.parentElement);
8728         var ctr = Roo.get(this.el.dom);
8729         
8730         var thd = this.getGridEl().select('thead',true).first();
8731         var tbd = this.getGridEl().select('tbody', true).first();
8732         var tfd = this.getGridEl().select('tfoot', true).first();
8733         
8734         var cw = ctr.getWidth();
8735         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8736         
8737         if (tbd) {
8738             
8739             tbd.setWidth(ctr.getWidth());
8740             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8741             // this needs fixing for various usage - currently only hydra job advers I think..
8742             //tdb.setHeight(
8743             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8744             //); 
8745             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8746             cw -= barsize;
8747         }
8748         cw = Math.max(cw, this.totalWidth);
8749         this.getGridEl().select('tbody tr',true).setWidth(cw);
8750         
8751         // resize 'expandable coloumn?
8752         
8753         return; // we doe not have a view in this design..
8754         
8755     },
8756     onBodyScroll: function()
8757     {
8758         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8759         if(this.mainHead){
8760             this.mainHead.setStyle({
8761                 'position' : 'relative',
8762                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8763             });
8764         }
8765         
8766         if(this.lazyLoad){
8767             
8768             var scrollHeight = this.mainBody.dom.scrollHeight;
8769             
8770             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8771             
8772             var height = this.mainBody.getHeight();
8773             
8774             if(scrollHeight - height == scrollTop) {
8775                 
8776                 var total = this.ds.getTotalCount();
8777                 
8778                 if(this.footer.cursor + this.footer.pageSize < total){
8779                     
8780                     this.footer.ds.load({
8781                         params : {
8782                             start : this.footer.cursor + this.footer.pageSize,
8783                             limit : this.footer.pageSize
8784                         },
8785                         add : true
8786                     });
8787                 }
8788             }
8789             
8790         }
8791     },
8792     
8793     onHeaderChange : function()
8794     {
8795         var header = this.renderHeader();
8796         var table = this.el.select('table', true).first();
8797         
8798         this.mainHead.remove();
8799         this.mainHead = table.createChild(header, this.mainBody, false);
8800     },
8801     
8802     onHiddenChange : function(colModel, colIndex, hidden)
8803     {
8804         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8805         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8806         
8807         this.CSS.updateRule(thSelector, "display", "");
8808         this.CSS.updateRule(tdSelector, "display", "");
8809         
8810         if(hidden){
8811             this.CSS.updateRule(thSelector, "display", "none");
8812             this.CSS.updateRule(tdSelector, "display", "none");
8813         }
8814         
8815         this.onHeaderChange();
8816         this.onLoad();
8817     },
8818     
8819     setColumnWidth: function(col_index, width)
8820     {
8821         // width = "md-2 xs-2..."
8822         if(!this.colModel.config[col_index]) {
8823             return;
8824         }
8825         
8826         var w = width.split(" ");
8827         
8828         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8829         
8830         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8831         
8832         
8833         for(var j = 0; j < w.length; j++) {
8834             
8835             if(!w[j]) {
8836                 continue;
8837             }
8838             
8839             var size_cls = w[j].split("-");
8840             
8841             if(!Number.isInteger(size_cls[1] * 1)) {
8842                 continue;
8843             }
8844             
8845             if(!this.colModel.config[col_index][size_cls[0]]) {
8846                 continue;
8847             }
8848             
8849             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8850                 continue;
8851             }
8852             
8853             h_row[0].classList.replace(
8854                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8855                 "col-"+size_cls[0]+"-"+size_cls[1]
8856             );
8857             
8858             for(var i = 0; i < rows.length; i++) {
8859                 
8860                 var size_cls = w[j].split("-");
8861                 
8862                 if(!Number.isInteger(size_cls[1] * 1)) {
8863                     continue;
8864                 }
8865                 
8866                 if(!this.colModel.config[col_index][size_cls[0]]) {
8867                     continue;
8868                 }
8869                 
8870                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8871                     continue;
8872                 }
8873                 
8874                 rows[i].classList.replace(
8875                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8876                     "col-"+size_cls[0]+"-"+size_cls[1]
8877                 );
8878             }
8879             
8880             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8881         }
8882     }
8883 });
8884
8885  
8886
8887  /*
8888  * - LGPL
8889  *
8890  * table cell
8891  * 
8892  */
8893
8894 /**
8895  * @class Roo.bootstrap.TableCell
8896  * @extends Roo.bootstrap.Component
8897  * Bootstrap TableCell class
8898  * @cfg {String} html cell contain text
8899  * @cfg {String} cls cell class
8900  * @cfg {String} tag cell tag (td|th) default td
8901  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8902  * @cfg {String} align Aligns the content in a cell
8903  * @cfg {String} axis Categorizes cells
8904  * @cfg {String} bgcolor Specifies the background color of a cell
8905  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8906  * @cfg {Number} colspan Specifies the number of columns a cell should span
8907  * @cfg {String} headers Specifies one or more header cells a cell is related to
8908  * @cfg {Number} height Sets the height of a cell
8909  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8910  * @cfg {Number} rowspan Sets the number of rows a cell should span
8911  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8912  * @cfg {String} valign Vertical aligns the content in a cell
8913  * @cfg {Number} width Specifies the width of a cell
8914  * 
8915  * @constructor
8916  * Create a new TableCell
8917  * @param {Object} config The config object
8918  */
8919
8920 Roo.bootstrap.TableCell = function(config){
8921     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8922 };
8923
8924 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8925     
8926     html: false,
8927     cls: false,
8928     tag: false,
8929     abbr: false,
8930     align: false,
8931     axis: false,
8932     bgcolor: false,
8933     charoff: false,
8934     colspan: false,
8935     headers: false,
8936     height: false,
8937     nowrap: false,
8938     rowspan: false,
8939     scope: false,
8940     valign: false,
8941     width: false,
8942     
8943     
8944     getAutoCreate : function(){
8945         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8946         
8947         cfg = {
8948             tag: 'td'
8949         };
8950         
8951         if(this.tag){
8952             cfg.tag = this.tag;
8953         }
8954         
8955         if (this.html) {
8956             cfg.html=this.html
8957         }
8958         if (this.cls) {
8959             cfg.cls=this.cls
8960         }
8961         if (this.abbr) {
8962             cfg.abbr=this.abbr
8963         }
8964         if (this.align) {
8965             cfg.align=this.align
8966         }
8967         if (this.axis) {
8968             cfg.axis=this.axis
8969         }
8970         if (this.bgcolor) {
8971             cfg.bgcolor=this.bgcolor
8972         }
8973         if (this.charoff) {
8974             cfg.charoff=this.charoff
8975         }
8976         if (this.colspan) {
8977             cfg.colspan=this.colspan
8978         }
8979         if (this.headers) {
8980             cfg.headers=this.headers
8981         }
8982         if (this.height) {
8983             cfg.height=this.height
8984         }
8985         if (this.nowrap) {
8986             cfg.nowrap=this.nowrap
8987         }
8988         if (this.rowspan) {
8989             cfg.rowspan=this.rowspan
8990         }
8991         if (this.scope) {
8992             cfg.scope=this.scope
8993         }
8994         if (this.valign) {
8995             cfg.valign=this.valign
8996         }
8997         if (this.width) {
8998             cfg.width=this.width
8999         }
9000         
9001         
9002         return cfg;
9003     }
9004    
9005 });
9006
9007  
9008
9009  /*
9010  * - LGPL
9011  *
9012  * table row
9013  * 
9014  */
9015
9016 /**
9017  * @class Roo.bootstrap.TableRow
9018  * @extends Roo.bootstrap.Component
9019  * Bootstrap TableRow class
9020  * @cfg {String} cls row class
9021  * @cfg {String} align Aligns the content in a table row
9022  * @cfg {String} bgcolor Specifies a background color for a table row
9023  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9024  * @cfg {String} valign Vertical aligns the content in a table row
9025  * 
9026  * @constructor
9027  * Create a new TableRow
9028  * @param {Object} config The config object
9029  */
9030
9031 Roo.bootstrap.TableRow = function(config){
9032     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9033 };
9034
9035 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9036     
9037     cls: false,
9038     align: false,
9039     bgcolor: false,
9040     charoff: false,
9041     valign: false,
9042     
9043     getAutoCreate : function(){
9044         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9045         
9046         cfg = {
9047             tag: 'tr'
9048         };
9049             
9050         if(this.cls){
9051             cfg.cls = this.cls;
9052         }
9053         if(this.align){
9054             cfg.align = this.align;
9055         }
9056         if(this.bgcolor){
9057             cfg.bgcolor = this.bgcolor;
9058         }
9059         if(this.charoff){
9060             cfg.charoff = this.charoff;
9061         }
9062         if(this.valign){
9063             cfg.valign = this.valign;
9064         }
9065         
9066         return cfg;
9067     }
9068    
9069 });
9070
9071  
9072
9073  /*
9074  * - LGPL
9075  *
9076  * table body
9077  * 
9078  */
9079
9080 /**
9081  * @class Roo.bootstrap.TableBody
9082  * @extends Roo.bootstrap.Component
9083  * Bootstrap TableBody class
9084  * @cfg {String} cls element class
9085  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9086  * @cfg {String} align Aligns the content inside the element
9087  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9088  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9089  * 
9090  * @constructor
9091  * Create a new TableBody
9092  * @param {Object} config The config object
9093  */
9094
9095 Roo.bootstrap.TableBody = function(config){
9096     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9097 };
9098
9099 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9100     
9101     cls: false,
9102     tag: false,
9103     align: false,
9104     charoff: false,
9105     valign: false,
9106     
9107     getAutoCreate : function(){
9108         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9109         
9110         cfg = {
9111             tag: 'tbody'
9112         };
9113             
9114         if (this.cls) {
9115             cfg.cls=this.cls
9116         }
9117         if(this.tag){
9118             cfg.tag = this.tag;
9119         }
9120         
9121         if(this.align){
9122             cfg.align = this.align;
9123         }
9124         if(this.charoff){
9125             cfg.charoff = this.charoff;
9126         }
9127         if(this.valign){
9128             cfg.valign = this.valign;
9129         }
9130         
9131         return cfg;
9132     }
9133     
9134     
9135 //    initEvents : function()
9136 //    {
9137 //        
9138 //        if(!this.store){
9139 //            return;
9140 //        }
9141 //        
9142 //        this.store = Roo.factory(this.store, Roo.data);
9143 //        this.store.on('load', this.onLoad, this);
9144 //        
9145 //        this.store.load();
9146 //        
9147 //    },
9148 //    
9149 //    onLoad: function () 
9150 //    {   
9151 //        this.fireEvent('load', this);
9152 //    }
9153 //    
9154 //   
9155 });
9156
9157  
9158
9159  /*
9160  * Based on:
9161  * Ext JS Library 1.1.1
9162  * Copyright(c) 2006-2007, Ext JS, LLC.
9163  *
9164  * Originally Released Under LGPL - original licence link has changed is not relivant.
9165  *
9166  * Fork - LGPL
9167  * <script type="text/javascript">
9168  */
9169
9170 // as we use this in bootstrap.
9171 Roo.namespace('Roo.form');
9172  /**
9173  * @class Roo.form.Action
9174  * Internal Class used to handle form actions
9175  * @constructor
9176  * @param {Roo.form.BasicForm} el The form element or its id
9177  * @param {Object} config Configuration options
9178  */
9179
9180  
9181  
9182 // define the action interface
9183 Roo.form.Action = function(form, options){
9184     this.form = form;
9185     this.options = options || {};
9186 };
9187 /**
9188  * Client Validation Failed
9189  * @const 
9190  */
9191 Roo.form.Action.CLIENT_INVALID = 'client';
9192 /**
9193  * Server Validation Failed
9194  * @const 
9195  */
9196 Roo.form.Action.SERVER_INVALID = 'server';
9197  /**
9198  * Connect to Server Failed
9199  * @const 
9200  */
9201 Roo.form.Action.CONNECT_FAILURE = 'connect';
9202 /**
9203  * Reading Data from Server Failed
9204  * @const 
9205  */
9206 Roo.form.Action.LOAD_FAILURE = 'load';
9207
9208 Roo.form.Action.prototype = {
9209     type : 'default',
9210     failureType : undefined,
9211     response : undefined,
9212     result : undefined,
9213
9214     // interface method
9215     run : function(options){
9216
9217     },
9218
9219     // interface method
9220     success : function(response){
9221
9222     },
9223
9224     // interface method
9225     handleResponse : function(response){
9226
9227     },
9228
9229     // default connection failure
9230     failure : function(response){
9231         
9232         this.response = response;
9233         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9234         this.form.afterAction(this, false);
9235     },
9236
9237     processResponse : function(response){
9238         this.response = response;
9239         if(!response.responseText){
9240             return true;
9241         }
9242         this.result = this.handleResponse(response);
9243         return this.result;
9244     },
9245
9246     // utility functions used internally
9247     getUrl : function(appendParams){
9248         var url = this.options.url || this.form.url || this.form.el.dom.action;
9249         if(appendParams){
9250             var p = this.getParams();
9251             if(p){
9252                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9253             }
9254         }
9255         return url;
9256     },
9257
9258     getMethod : function(){
9259         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9260     },
9261
9262     getParams : function(){
9263         var bp = this.form.baseParams;
9264         var p = this.options.params;
9265         if(p){
9266             if(typeof p == "object"){
9267                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9268             }else if(typeof p == 'string' && bp){
9269                 p += '&' + Roo.urlEncode(bp);
9270             }
9271         }else if(bp){
9272             p = Roo.urlEncode(bp);
9273         }
9274         return p;
9275     },
9276
9277     createCallback : function(){
9278         return {
9279             success: this.success,
9280             failure: this.failure,
9281             scope: this,
9282             timeout: (this.form.timeout*1000),
9283             upload: this.form.fileUpload ? this.success : undefined
9284         };
9285     }
9286 };
9287
9288 Roo.form.Action.Submit = function(form, options){
9289     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9290 };
9291
9292 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9293     type : 'submit',
9294
9295     haveProgress : false,
9296     uploadComplete : false,
9297     
9298     // uploadProgress indicator.
9299     uploadProgress : function()
9300     {
9301         if (!this.form.progressUrl) {
9302             return;
9303         }
9304         
9305         if (!this.haveProgress) {
9306             Roo.MessageBox.progress("Uploading", "Uploading");
9307         }
9308         if (this.uploadComplete) {
9309            Roo.MessageBox.hide();
9310            return;
9311         }
9312         
9313         this.haveProgress = true;
9314    
9315         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9316         
9317         var c = new Roo.data.Connection();
9318         c.request({
9319             url : this.form.progressUrl,
9320             params: {
9321                 id : uid
9322             },
9323             method: 'GET',
9324             success : function(req){
9325                //console.log(data);
9326                 var rdata = false;
9327                 var edata;
9328                 try  {
9329                    rdata = Roo.decode(req.responseText)
9330                 } catch (e) {
9331                     Roo.log("Invalid data from server..");
9332                     Roo.log(edata);
9333                     return;
9334                 }
9335                 if (!rdata || !rdata.success) {
9336                     Roo.log(rdata);
9337                     Roo.MessageBox.alert(Roo.encode(rdata));
9338                     return;
9339                 }
9340                 var data = rdata.data;
9341                 
9342                 if (this.uploadComplete) {
9343                    Roo.MessageBox.hide();
9344                    return;
9345                 }
9346                    
9347                 if (data){
9348                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9349                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9350                     );
9351                 }
9352                 this.uploadProgress.defer(2000,this);
9353             },
9354        
9355             failure: function(data) {
9356                 Roo.log('progress url failed ');
9357                 Roo.log(data);
9358             },
9359             scope : this
9360         });
9361            
9362     },
9363     
9364     
9365     run : function()
9366     {
9367         // run get Values on the form, so it syncs any secondary forms.
9368         this.form.getValues();
9369         
9370         var o = this.options;
9371         var method = this.getMethod();
9372         var isPost = method == 'POST';
9373         if(o.clientValidation === false || this.form.isValid()){
9374             
9375             if (this.form.progressUrl) {
9376                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9377                     (new Date() * 1) + '' + Math.random());
9378                     
9379             } 
9380             
9381             
9382             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9383                 form:this.form.el.dom,
9384                 url:this.getUrl(!isPost),
9385                 method: method,
9386                 params:isPost ? this.getParams() : null,
9387                 isUpload: this.form.fileUpload,
9388                 formData : this.form.formData
9389             }));
9390             
9391             this.uploadProgress();
9392
9393         }else if (o.clientValidation !== false){ // client validation failed
9394             this.failureType = Roo.form.Action.CLIENT_INVALID;
9395             this.form.afterAction(this, false);
9396         }
9397     },
9398
9399     success : function(response)
9400     {
9401         this.uploadComplete= true;
9402         if (this.haveProgress) {
9403             Roo.MessageBox.hide();
9404         }
9405         
9406         
9407         var result = this.processResponse(response);
9408         if(result === true || result.success){
9409             this.form.afterAction(this, true);
9410             return;
9411         }
9412         if(result.errors){
9413             this.form.markInvalid(result.errors);
9414             this.failureType = Roo.form.Action.SERVER_INVALID;
9415         }
9416         this.form.afterAction(this, false);
9417     },
9418     failure : function(response)
9419     {
9420         this.uploadComplete= true;
9421         if (this.haveProgress) {
9422             Roo.MessageBox.hide();
9423         }
9424         
9425         this.response = response;
9426         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9427         this.form.afterAction(this, false);
9428     },
9429     
9430     handleResponse : function(response){
9431         if(this.form.errorReader){
9432             var rs = this.form.errorReader.read(response);
9433             var errors = [];
9434             if(rs.records){
9435                 for(var i = 0, len = rs.records.length; i < len; i++) {
9436                     var r = rs.records[i];
9437                     errors[i] = r.data;
9438                 }
9439             }
9440             if(errors.length < 1){
9441                 errors = null;
9442             }
9443             return {
9444                 success : rs.success,
9445                 errors : errors
9446             };
9447         }
9448         var ret = false;
9449         try {
9450             ret = Roo.decode(response.responseText);
9451         } catch (e) {
9452             ret = {
9453                 success: false,
9454                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9455                 errors : []
9456             };
9457         }
9458         return ret;
9459         
9460     }
9461 });
9462
9463
9464 Roo.form.Action.Load = function(form, options){
9465     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9466     this.reader = this.form.reader;
9467 };
9468
9469 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9470     type : 'load',
9471
9472     run : function(){
9473         
9474         Roo.Ajax.request(Roo.apply(
9475                 this.createCallback(), {
9476                     method:this.getMethod(),
9477                     url:this.getUrl(false),
9478                     params:this.getParams()
9479         }));
9480     },
9481
9482     success : function(response){
9483         
9484         var result = this.processResponse(response);
9485         if(result === true || !result.success || !result.data){
9486             this.failureType = Roo.form.Action.LOAD_FAILURE;
9487             this.form.afterAction(this, false);
9488             return;
9489         }
9490         this.form.clearInvalid();
9491         this.form.setValues(result.data);
9492         this.form.afterAction(this, true);
9493     },
9494
9495     handleResponse : function(response){
9496         if(this.form.reader){
9497             var rs = this.form.reader.read(response);
9498             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9499             return {
9500                 success : rs.success,
9501                 data : data
9502             };
9503         }
9504         return Roo.decode(response.responseText);
9505     }
9506 });
9507
9508 Roo.form.Action.ACTION_TYPES = {
9509     'load' : Roo.form.Action.Load,
9510     'submit' : Roo.form.Action.Submit
9511 };/*
9512  * - LGPL
9513  *
9514  * form
9515  *
9516  */
9517
9518 /**
9519  * @class Roo.bootstrap.Form
9520  * @extends Roo.bootstrap.Component
9521  * Bootstrap Form class
9522  * @cfg {String} method  GET | POST (default POST)
9523  * @cfg {String} labelAlign top | left (default top)
9524  * @cfg {String} align left  | right - for navbars
9525  * @cfg {Boolean} loadMask load mask when submit (default true)
9526
9527  *
9528  * @constructor
9529  * Create a new Form
9530  * @param {Object} config The config object
9531  */
9532
9533
9534 Roo.bootstrap.Form = function(config){
9535     
9536     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9537     
9538     Roo.bootstrap.Form.popover.apply();
9539     
9540     this.addEvents({
9541         /**
9542          * @event clientvalidation
9543          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9544          * @param {Form} this
9545          * @param {Boolean} valid true if the form has passed client-side validation
9546          */
9547         clientvalidation: true,
9548         /**
9549          * @event beforeaction
9550          * Fires before any action is performed. Return false to cancel the action.
9551          * @param {Form} this
9552          * @param {Action} action The action to be performed
9553          */
9554         beforeaction: true,
9555         /**
9556          * @event actionfailed
9557          * Fires when an action fails.
9558          * @param {Form} this
9559          * @param {Action} action The action that failed
9560          */
9561         actionfailed : true,
9562         /**
9563          * @event actioncomplete
9564          * Fires when an action is completed.
9565          * @param {Form} this
9566          * @param {Action} action The action that completed
9567          */
9568         actioncomplete : true
9569     });
9570 };
9571
9572 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9573
9574      /**
9575      * @cfg {String} method
9576      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9577      */
9578     method : 'POST',
9579     /**
9580      * @cfg {String} url
9581      * The URL to use for form actions if one isn't supplied in the action options.
9582      */
9583     /**
9584      * @cfg {Boolean} fileUpload
9585      * Set to true if this form is a file upload.
9586      */
9587
9588     /**
9589      * @cfg {Object} baseParams
9590      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9591      */
9592
9593     /**
9594      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9595      */
9596     timeout: 30,
9597     /**
9598      * @cfg {Sting} align (left|right) for navbar forms
9599      */
9600     align : 'left',
9601
9602     // private
9603     activeAction : null,
9604
9605     /**
9606      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9607      * element by passing it or its id or mask the form itself by passing in true.
9608      * @type Mixed
9609      */
9610     waitMsgTarget : false,
9611
9612     loadMask : true,
9613     
9614     /**
9615      * @cfg {Boolean} errorMask (true|false) default false
9616      */
9617     errorMask : false,
9618     
9619     /**
9620      * @cfg {Number} maskOffset Default 100
9621      */
9622     maskOffset : 100,
9623     
9624     /**
9625      * @cfg {Boolean} maskBody
9626      */
9627     maskBody : false,
9628
9629     getAutoCreate : function(){
9630
9631         var cfg = {
9632             tag: 'form',
9633             method : this.method || 'POST',
9634             id : this.id || Roo.id(),
9635             cls : ''
9636         };
9637         if (this.parent().xtype.match(/^Nav/)) {
9638             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9639
9640         }
9641
9642         if (this.labelAlign == 'left' ) {
9643             cfg.cls += ' form-horizontal';
9644         }
9645
9646
9647         return cfg;
9648     },
9649     initEvents : function()
9650     {
9651         this.el.on('submit', this.onSubmit, this);
9652         // this was added as random key presses on the form where triggering form submit.
9653         this.el.on('keypress', function(e) {
9654             if (e.getCharCode() != 13) {
9655                 return true;
9656             }
9657             // we might need to allow it for textareas.. and some other items.
9658             // check e.getTarget().
9659
9660             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9661                 return true;
9662             }
9663
9664             Roo.log("keypress blocked");
9665
9666             e.preventDefault();
9667             return false;
9668         });
9669         
9670     },
9671     // private
9672     onSubmit : function(e){
9673         e.stopEvent();
9674     },
9675
9676      /**
9677      * Returns true if client-side validation on the form is successful.
9678      * @return Boolean
9679      */
9680     isValid : function(){
9681         var items = this.getItems();
9682         var valid = true;
9683         var target = false;
9684         
9685         items.each(function(f){
9686             
9687             if(f.validate()){
9688                 return;
9689             }
9690             
9691             Roo.log('invalid field: ' + f.name);
9692             
9693             valid = false;
9694
9695             if(!target && f.el.isVisible(true)){
9696                 target = f;
9697             }
9698            
9699         });
9700         
9701         if(this.errorMask && !valid){
9702             Roo.bootstrap.Form.popover.mask(this, target);
9703         }
9704         
9705         return valid;
9706     },
9707     
9708     /**
9709      * Returns true if any fields in this form have changed since their original load.
9710      * @return Boolean
9711      */
9712     isDirty : function(){
9713         var dirty = false;
9714         var items = this.getItems();
9715         items.each(function(f){
9716            if(f.isDirty()){
9717                dirty = true;
9718                return false;
9719            }
9720            return true;
9721         });
9722         return dirty;
9723     },
9724      /**
9725      * Performs a predefined action (submit or load) or custom actions you define on this form.
9726      * @param {String} actionName The name of the action type
9727      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9728      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9729      * accept other config options):
9730      * <pre>
9731 Property          Type             Description
9732 ----------------  ---------------  ----------------------------------------------------------------------------------
9733 url               String           The url for the action (defaults to the form's url)
9734 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9735 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9736 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9737                                    validate the form on the client (defaults to false)
9738      * </pre>
9739      * @return {BasicForm} this
9740      */
9741     doAction : function(action, options){
9742         if(typeof action == 'string'){
9743             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9744         }
9745         if(this.fireEvent('beforeaction', this, action) !== false){
9746             this.beforeAction(action);
9747             action.run.defer(100, action);
9748         }
9749         return this;
9750     },
9751
9752     // private
9753     beforeAction : function(action){
9754         var o = action.options;
9755         
9756         if(this.loadMask){
9757             
9758             if(this.maskBody){
9759                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9760             } else {
9761                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9762             }
9763         }
9764         // not really supported yet.. ??
9765
9766         //if(this.waitMsgTarget === true){
9767         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768         //}else if(this.waitMsgTarget){
9769         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9770         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else {
9772         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9773        // }
9774
9775     },
9776
9777     // private
9778     afterAction : function(action, success){
9779         this.activeAction = null;
9780         var o = action.options;
9781
9782         if(this.loadMask){
9783             
9784             if(this.maskBody){
9785                 Roo.get(document.body).unmask();
9786             } else {
9787                 this.el.unmask();
9788             }
9789         }
9790         
9791         //if(this.waitMsgTarget === true){
9792 //            this.el.unmask();
9793         //}else if(this.waitMsgTarget){
9794         //    this.waitMsgTarget.unmask();
9795         //}else{
9796         //    Roo.MessageBox.updateProgress(1);
9797         //    Roo.MessageBox.hide();
9798        // }
9799         //
9800         if(success){
9801             if(o.reset){
9802                 this.reset();
9803             }
9804             Roo.callback(o.success, o.scope, [this, action]);
9805             this.fireEvent('actioncomplete', this, action);
9806
9807         }else{
9808
9809             // failure condition..
9810             // we have a scenario where updates need confirming.
9811             // eg. if a locking scenario exists..
9812             // we look for { errors : { needs_confirm : true }} in the response.
9813             if (
9814                 (typeof(action.result) != 'undefined')  &&
9815                 (typeof(action.result.errors) != 'undefined')  &&
9816                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9817            ){
9818                 var _t = this;
9819                 Roo.log("not supported yet");
9820                  /*
9821
9822                 Roo.MessageBox.confirm(
9823                     "Change requires confirmation",
9824                     action.result.errorMsg,
9825                     function(r) {
9826                         if (r != 'yes') {
9827                             return;
9828                         }
9829                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9830                     }
9831
9832                 );
9833                 */
9834
9835
9836                 return;
9837             }
9838
9839             Roo.callback(o.failure, o.scope, [this, action]);
9840             // show an error message if no failed handler is set..
9841             if (!this.hasListener('actionfailed')) {
9842                 Roo.log("need to add dialog support");
9843                 /*
9844                 Roo.MessageBox.alert("Error",
9845                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9846                         action.result.errorMsg :
9847                         "Saving Failed, please check your entries or try again"
9848                 );
9849                 */
9850             }
9851
9852             this.fireEvent('actionfailed', this, action);
9853         }
9854
9855     },
9856     /**
9857      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9858      * @param {String} id The value to search for
9859      * @return Field
9860      */
9861     findField : function(id){
9862         var items = this.getItems();
9863         var field = items.get(id);
9864         if(!field){
9865              items.each(function(f){
9866                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9867                     field = f;
9868                     return false;
9869                 }
9870                 return true;
9871             });
9872         }
9873         return field || null;
9874     },
9875      /**
9876      * Mark fields in this form invalid in bulk.
9877      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9878      * @return {BasicForm} this
9879      */
9880     markInvalid : function(errors){
9881         if(errors instanceof Array){
9882             for(var i = 0, len = errors.length; i < len; i++){
9883                 var fieldError = errors[i];
9884                 var f = this.findField(fieldError.id);
9885                 if(f){
9886                     f.markInvalid(fieldError.msg);
9887                 }
9888             }
9889         }else{
9890             var field, id;
9891             for(id in errors){
9892                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9893                     field.markInvalid(errors[id]);
9894                 }
9895             }
9896         }
9897         //Roo.each(this.childForms || [], function (f) {
9898         //    f.markInvalid(errors);
9899         //});
9900
9901         return this;
9902     },
9903
9904     /**
9905      * Set values for fields in this form in bulk.
9906      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9907      * @return {BasicForm} this
9908      */
9909     setValues : function(values){
9910         if(values instanceof Array){ // array of objects
9911             for(var i = 0, len = values.length; i < len; i++){
9912                 var v = values[i];
9913                 var f = this.findField(v.id);
9914                 if(f){
9915                     f.setValue(v.value);
9916                     if(this.trackResetOnLoad){
9917                         f.originalValue = f.getValue();
9918                     }
9919                 }
9920             }
9921         }else{ // object hash
9922             var field, id;
9923             for(id in values){
9924                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9925
9926                     if (field.setFromData &&
9927                         field.valueField &&
9928                         field.displayField &&
9929                         // combos' with local stores can
9930                         // be queried via setValue()
9931                         // to set their value..
9932                         (field.store && !field.store.isLocal)
9933                         ) {
9934                         // it's a combo
9935                         var sd = { };
9936                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9937                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9938                         field.setFromData(sd);
9939
9940                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9941                         
9942                         field.setFromData(values);
9943                         
9944                     } else {
9945                         field.setValue(values[id]);
9946                     }
9947
9948
9949                     if(this.trackResetOnLoad){
9950                         field.originalValue = field.getValue();
9951                     }
9952                 }
9953             }
9954         }
9955
9956         //Roo.each(this.childForms || [], function (f) {
9957         //    f.setValues(values);
9958         //});
9959
9960         return this;
9961     },
9962
9963     /**
9964      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9965      * they are returned as an array.
9966      * @param {Boolean} asString
9967      * @return {Object}
9968      */
9969     getValues : function(asString){
9970         //if (this.childForms) {
9971             // copy values from the child forms
9972         //    Roo.each(this.childForms, function (f) {
9973         //        this.setValues(f.getValues());
9974         //    }, this);
9975         //}
9976
9977
9978
9979         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9980         if(asString === true){
9981             return fs;
9982         }
9983         return Roo.urlDecode(fs);
9984     },
9985
9986     /**
9987      * Returns the fields in this form as an object with key/value pairs.
9988      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9989      * @return {Object}
9990      */
9991     getFieldValues : function(with_hidden)
9992     {
9993         var items = this.getItems();
9994         var ret = {};
9995         items.each(function(f){
9996             
9997             if (!f.getName()) {
9998                 return;
9999             }
10000             
10001             var v = f.getValue();
10002             
10003             if (f.inputType =='radio') {
10004                 if (typeof(ret[f.getName()]) == 'undefined') {
10005                     ret[f.getName()] = ''; // empty..
10006                 }
10007
10008                 if (!f.el.dom.checked) {
10009                     return;
10010
10011                 }
10012                 v = f.el.dom.value;
10013
10014             }
10015             
10016             if(f.xtype == 'MoneyField'){
10017                 ret[f.currencyName] = f.getCurrency();
10018             }
10019
10020             // not sure if this supported any more..
10021             if ((typeof(v) == 'object') && f.getRawValue) {
10022                 v = f.getRawValue() ; // dates..
10023             }
10024             // combo boxes where name != hiddenName...
10025             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10026                 ret[f.name] = f.getRawValue();
10027             }
10028             ret[f.getName()] = v;
10029         });
10030
10031         return ret;
10032     },
10033
10034     /**
10035      * Clears all invalid messages in this form.
10036      * @return {BasicForm} this
10037      */
10038     clearInvalid : function(){
10039         var items = this.getItems();
10040
10041         items.each(function(f){
10042            f.clearInvalid();
10043         });
10044
10045         return this;
10046     },
10047
10048     /**
10049      * Resets this form.
10050      * @return {BasicForm} this
10051      */
10052     reset : function(){
10053         var items = this.getItems();
10054         items.each(function(f){
10055             f.reset();
10056         });
10057
10058         Roo.each(this.childForms || [], function (f) {
10059             f.reset();
10060         });
10061
10062
10063         return this;
10064     },
10065     
10066     getItems : function()
10067     {
10068         var r=new Roo.util.MixedCollection(false, function(o){
10069             return o.id || (o.id = Roo.id());
10070         });
10071         var iter = function(el) {
10072             if (el.inputEl) {
10073                 r.add(el);
10074             }
10075             if (!el.items) {
10076                 return;
10077             }
10078             Roo.each(el.items,function(e) {
10079                 iter(e);
10080             });
10081         };
10082
10083         iter(this);
10084         return r;
10085     },
10086     
10087     hideFields : function(items)
10088     {
10089         Roo.each(items, function(i){
10090             
10091             var f = this.findField(i);
10092             
10093             if(!f){
10094                 return;
10095             }
10096             
10097             f.hide();
10098             
10099         }, this);
10100     },
10101     
10102     showFields : function(items)
10103     {
10104         Roo.each(items, function(i){
10105             
10106             var f = this.findField(i);
10107             
10108             if(!f){
10109                 return;
10110             }
10111             
10112             f.show();
10113             
10114         }, this);
10115     }
10116
10117 });
10118
10119 Roo.apply(Roo.bootstrap.Form, {
10120     
10121     popover : {
10122         
10123         padding : 5,
10124         
10125         isApplied : false,
10126         
10127         isMasked : false,
10128         
10129         form : false,
10130         
10131         target : false,
10132         
10133         toolTip : false,
10134         
10135         intervalID : false,
10136         
10137         maskEl : false,
10138         
10139         apply : function()
10140         {
10141             if(this.isApplied){
10142                 return;
10143             }
10144             
10145             this.maskEl = {
10146                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10147                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10148                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10149                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10150             };
10151             
10152             this.maskEl.top.enableDisplayMode("block");
10153             this.maskEl.left.enableDisplayMode("block");
10154             this.maskEl.bottom.enableDisplayMode("block");
10155             this.maskEl.right.enableDisplayMode("block");
10156             
10157             this.toolTip = new Roo.bootstrap.Tooltip({
10158                 cls : 'roo-form-error-popover',
10159                 alignment : {
10160                     'left' : ['r-l', [-2,0], 'right'],
10161                     'right' : ['l-r', [2,0], 'left'],
10162                     'bottom' : ['tl-bl', [0,2], 'top'],
10163                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10164                 }
10165             });
10166             
10167             this.toolTip.render(Roo.get(document.body));
10168
10169             this.toolTip.el.enableDisplayMode("block");
10170             
10171             Roo.get(document.body).on('click', function(){
10172                 this.unmask();
10173             }, this);
10174             
10175             Roo.get(document.body).on('touchstart', function(){
10176                 this.unmask();
10177             }, this);
10178             
10179             this.isApplied = true
10180         },
10181         
10182         mask : function(form, target)
10183         {
10184             this.form = form;
10185             
10186             this.target = target;
10187             
10188             if(!this.form.errorMask || !target.el){
10189                 return;
10190             }
10191             
10192             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10193             
10194             Roo.log(scrollable);
10195             
10196             var ot = this.target.el.calcOffsetsTo(scrollable);
10197             
10198             var scrollTo = ot[1] - this.form.maskOffset;
10199             
10200             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10201             
10202             scrollable.scrollTo('top', scrollTo);
10203             
10204             var box = this.target.el.getBox();
10205             Roo.log(box);
10206             var zIndex = Roo.bootstrap.Modal.zIndex++;
10207
10208             
10209             this.maskEl.top.setStyle('position', 'absolute');
10210             this.maskEl.top.setStyle('z-index', zIndex);
10211             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10212             this.maskEl.top.setLeft(0);
10213             this.maskEl.top.setTop(0);
10214             this.maskEl.top.show();
10215             
10216             this.maskEl.left.setStyle('position', 'absolute');
10217             this.maskEl.left.setStyle('z-index', zIndex);
10218             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10219             this.maskEl.left.setLeft(0);
10220             this.maskEl.left.setTop(box.y - this.padding);
10221             this.maskEl.left.show();
10222
10223             this.maskEl.bottom.setStyle('position', 'absolute');
10224             this.maskEl.bottom.setStyle('z-index', zIndex);
10225             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10226             this.maskEl.bottom.setLeft(0);
10227             this.maskEl.bottom.setTop(box.bottom + this.padding);
10228             this.maskEl.bottom.show();
10229
10230             this.maskEl.right.setStyle('position', 'absolute');
10231             this.maskEl.right.setStyle('z-index', zIndex);
10232             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10233             this.maskEl.right.setLeft(box.right + this.padding);
10234             this.maskEl.right.setTop(box.y - this.padding);
10235             this.maskEl.right.show();
10236
10237             this.toolTip.bindEl = this.target.el;
10238
10239             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10240
10241             var tip = this.target.blankText;
10242
10243             if(this.target.getValue() !== '' ) {
10244                 
10245                 if (this.target.invalidText.length) {
10246                     tip = this.target.invalidText;
10247                 } else if (this.target.regexText.length){
10248                     tip = this.target.regexText;
10249                 }
10250             }
10251
10252             this.toolTip.show(tip);
10253
10254             this.intervalID = window.setInterval(function() {
10255                 Roo.bootstrap.Form.popover.unmask();
10256             }, 10000);
10257
10258             window.onwheel = function(){ return false;};
10259             
10260             (function(){ this.isMasked = true; }).defer(500, this);
10261             
10262         },
10263         
10264         unmask : function()
10265         {
10266             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10267                 return;
10268             }
10269             
10270             this.maskEl.top.setStyle('position', 'absolute');
10271             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10272             this.maskEl.top.hide();
10273
10274             this.maskEl.left.setStyle('position', 'absolute');
10275             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10276             this.maskEl.left.hide();
10277
10278             this.maskEl.bottom.setStyle('position', 'absolute');
10279             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10280             this.maskEl.bottom.hide();
10281
10282             this.maskEl.right.setStyle('position', 'absolute');
10283             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10284             this.maskEl.right.hide();
10285             
10286             this.toolTip.hide();
10287             
10288             this.toolTip.el.hide();
10289             
10290             window.onwheel = function(){ return true;};
10291             
10292             if(this.intervalID){
10293                 window.clearInterval(this.intervalID);
10294                 this.intervalID = false;
10295             }
10296             
10297             this.isMasked = false;
10298             
10299         }
10300         
10301     }
10302     
10303 });
10304
10305 /*
10306  * Based on:
10307  * Ext JS Library 1.1.1
10308  * Copyright(c) 2006-2007, Ext JS, LLC.
10309  *
10310  * Originally Released Under LGPL - original licence link has changed is not relivant.
10311  *
10312  * Fork - LGPL
10313  * <script type="text/javascript">
10314  */
10315 /**
10316  * @class Roo.form.VTypes
10317  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10318  * @singleton
10319  */
10320 Roo.form.VTypes = function(){
10321     // closure these in so they are only created once.
10322     var alpha = /^[a-zA-Z_]+$/;
10323     var alphanum = /^[a-zA-Z0-9_]+$/;
10324     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10325     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10326
10327     // All these messages and functions are configurable
10328     return {
10329         /**
10330          * The function used to validate email addresses
10331          * @param {String} value The email address
10332          */
10333         'email' : function(v){
10334             return email.test(v);
10335         },
10336         /**
10337          * The error text to display when the email validation function returns false
10338          * @type String
10339          */
10340         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10341         /**
10342          * The keystroke filter mask to be applied on email input
10343          * @type RegExp
10344          */
10345         'emailMask' : /[a-z0-9_\.\-@]/i,
10346
10347         /**
10348          * The function used to validate URLs
10349          * @param {String} value The URL
10350          */
10351         'url' : function(v){
10352             return url.test(v);
10353         },
10354         /**
10355          * The error text to display when the url validation function returns false
10356          * @type String
10357          */
10358         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10359         
10360         /**
10361          * The function used to validate alpha values
10362          * @param {String} value The value
10363          */
10364         'alpha' : function(v){
10365             return alpha.test(v);
10366         },
10367         /**
10368          * The error text to display when the alpha validation function returns false
10369          * @type String
10370          */
10371         'alphaText' : 'This field should only contain letters and _',
10372         /**
10373          * The keystroke filter mask to be applied on alpha input
10374          * @type RegExp
10375          */
10376         'alphaMask' : /[a-z_]/i,
10377
10378         /**
10379          * The function used to validate alphanumeric values
10380          * @param {String} value The value
10381          */
10382         'alphanum' : function(v){
10383             return alphanum.test(v);
10384         },
10385         /**
10386          * The error text to display when the alphanumeric validation function returns false
10387          * @type String
10388          */
10389         'alphanumText' : 'This field should only contain letters, numbers and _',
10390         /**
10391          * The keystroke filter mask to be applied on alphanumeric input
10392          * @type RegExp
10393          */
10394         'alphanumMask' : /[a-z0-9_]/i
10395     };
10396 }();/*
10397  * - LGPL
10398  *
10399  * Input
10400  * 
10401  */
10402
10403 /**
10404  * @class Roo.bootstrap.Input
10405  * @extends Roo.bootstrap.Component
10406  * Bootstrap Input class
10407  * @cfg {Boolean} disabled is it disabled
10408  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10409  * @cfg {String} name name of the input
10410  * @cfg {string} fieldLabel - the label associated
10411  * @cfg {string} placeholder - placeholder to put in text.
10412  * @cfg {string}  before - input group add on before
10413  * @cfg {string} after - input group add on after
10414  * @cfg {string} size - (lg|sm) or leave empty..
10415  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10416  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10417  * @cfg {Number} md colspan out of 12 for computer-sized screens
10418  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10419  * @cfg {string} value default value of the input
10420  * @cfg {Number} labelWidth set the width of label 
10421  * @cfg {Number} labellg set the width of label (1-12)
10422  * @cfg {Number} labelmd set the width of label (1-12)
10423  * @cfg {Number} labelsm set the width of label (1-12)
10424  * @cfg {Number} labelxs set the width of label (1-12)
10425  * @cfg {String} labelAlign (top|left)
10426  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10427  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10428  * @cfg {String} indicatorpos (left|right) default left
10429  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10430  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10431  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10432
10433  * @cfg {String} align (left|center|right) Default left
10434  * @cfg {Boolean} forceFeedback (true|false) Default false
10435  * 
10436  * @constructor
10437  * Create a new Input
10438  * @param {Object} config The config object
10439  */
10440
10441 Roo.bootstrap.Input = function(config){
10442     
10443     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10444     
10445     this.addEvents({
10446         /**
10447          * @event focus
10448          * Fires when this field receives input focus.
10449          * @param {Roo.form.Field} this
10450          */
10451         focus : true,
10452         /**
10453          * @event blur
10454          * Fires when this field loses input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         blur : true,
10458         /**
10459          * @event specialkey
10460          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10461          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10462          * @param {Roo.form.Field} this
10463          * @param {Roo.EventObject} e The event object
10464          */
10465         specialkey : true,
10466         /**
10467          * @event change
10468          * Fires just before the field blurs if the field value has changed.
10469          * @param {Roo.form.Field} this
10470          * @param {Mixed} newValue The new value
10471          * @param {Mixed} oldValue The original value
10472          */
10473         change : true,
10474         /**
10475          * @event invalid
10476          * Fires after the field has been marked as invalid.
10477          * @param {Roo.form.Field} this
10478          * @param {String} msg The validation message
10479          */
10480         invalid : true,
10481         /**
10482          * @event valid
10483          * Fires after the field has been validated with no errors.
10484          * @param {Roo.form.Field} this
10485          */
10486         valid : true,
10487          /**
10488          * @event keyup
10489          * Fires after the key up
10490          * @param {Roo.form.Field} this
10491          * @param {Roo.EventObject}  e The event Object
10492          */
10493         keyup : true
10494     });
10495 };
10496
10497 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10498      /**
10499      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10500       automatic validation (defaults to "keyup").
10501      */
10502     validationEvent : "keyup",
10503      /**
10504      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10505      */
10506     validateOnBlur : true,
10507     /**
10508      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10509      */
10510     validationDelay : 250,
10511      /**
10512      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10513      */
10514     focusClass : "x-form-focus",  // not needed???
10515     
10516        
10517     /**
10518      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10519      */
10520     invalidClass : "has-warning",
10521     
10522     /**
10523      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10524      */
10525     validClass : "has-success",
10526     
10527     /**
10528      * @cfg {Boolean} hasFeedback (true|false) default true
10529      */
10530     hasFeedback : true,
10531     
10532     /**
10533      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10534      */
10535     invalidFeedbackClass : "glyphicon-warning-sign",
10536     
10537     /**
10538      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10539      */
10540     validFeedbackClass : "glyphicon-ok",
10541     
10542     /**
10543      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10544      */
10545     selectOnFocus : false,
10546     
10547      /**
10548      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10549      */
10550     maskRe : null,
10551        /**
10552      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10553      */
10554     vtype : null,
10555     
10556       /**
10557      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10558      */
10559     disableKeyFilter : false,
10560     
10561        /**
10562      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10563      */
10564     disabled : false,
10565      /**
10566      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10567      */
10568     allowBlank : true,
10569     /**
10570      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10571      */
10572     blankText : "Please complete this mandatory field",
10573     
10574      /**
10575      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10576      */
10577     minLength : 0,
10578     /**
10579      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10580      */
10581     maxLength : Number.MAX_VALUE,
10582     /**
10583      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10584      */
10585     minLengthText : "The minimum length for this field is {0}",
10586     /**
10587      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10588      */
10589     maxLengthText : "The maximum length for this field is {0}",
10590   
10591     
10592     /**
10593      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10594      * If available, this function will be called only after the basic validators all return true, and will be passed the
10595      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10596      */
10597     validator : null,
10598     /**
10599      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10600      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10601      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10602      */
10603     regex : null,
10604     /**
10605      * @cfg {String} regexText -- Depricated - use Invalid Text
10606      */
10607     regexText : "",
10608     
10609     /**
10610      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10611      */
10612     invalidText : "",
10613     
10614     
10615     
10616     autocomplete: false,
10617     
10618     
10619     fieldLabel : '',
10620     inputType : 'text',
10621     
10622     name : false,
10623     placeholder: false,
10624     before : false,
10625     after : false,
10626     size : false,
10627     hasFocus : false,
10628     preventMark: false,
10629     isFormField : true,
10630     value : '',
10631     labelWidth : 2,
10632     labelAlign : false,
10633     readOnly : false,
10634     align : false,
10635     formatedValue : false,
10636     forceFeedback : false,
10637     
10638     indicatorpos : 'left',
10639     
10640     labellg : 0,
10641     labelmd : 0,
10642     labelsm : 0,
10643     labelxs : 0,
10644     
10645     capture : '',
10646     accept : '',
10647     
10648     parentLabelAlign : function()
10649     {
10650         var parent = this;
10651         while (parent.parent()) {
10652             parent = parent.parent();
10653             if (typeof(parent.labelAlign) !='undefined') {
10654                 return parent.labelAlign;
10655             }
10656         }
10657         return 'left';
10658         
10659     },
10660     
10661     getAutoCreate : function()
10662     {
10663         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10664         
10665         var id = Roo.id();
10666         
10667         var cfg = {};
10668         
10669         if(this.inputType != 'hidden'){
10670             cfg.cls = 'form-group' //input-group
10671         }
10672         
10673         var input =  {
10674             tag: 'input',
10675             id : id,
10676             type : this.inputType,
10677             value : this.value,
10678             cls : 'form-control',
10679             placeholder : this.placeholder || '',
10680             autocomplete : this.autocomplete || 'new-password'
10681         };
10682         if (this.inputType == 'file') {
10683             input.style = 'overflow:hidden'; // why not in CSS?
10684         }
10685         
10686         if(this.capture.length){
10687             input.capture = this.capture;
10688         }
10689         
10690         if(this.accept.length){
10691             input.accept = this.accept + "/*";
10692         }
10693         
10694         if(this.align){
10695             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10696         }
10697         
10698         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10699             input.maxLength = this.maxLength;
10700         }
10701         
10702         if (this.disabled) {
10703             input.disabled=true;
10704         }
10705         
10706         if (this.readOnly) {
10707             input.readonly=true;
10708         }
10709         
10710         if (this.name) {
10711             input.name = this.name;
10712         }
10713         
10714         if (this.size) {
10715             input.cls += ' input-' + this.size;
10716         }
10717         
10718         var settings=this;
10719         ['xs','sm','md','lg'].map(function(size){
10720             if (settings[size]) {
10721                 cfg.cls += ' col-' + size + '-' + settings[size];
10722             }
10723         });
10724         
10725         var inputblock = input;
10726         
10727         var feedback = {
10728             tag: 'span',
10729             cls: 'glyphicon form-control-feedback'
10730         };
10731             
10732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10733             
10734             inputblock = {
10735                 cls : 'has-feedback',
10736                 cn :  [
10737                     input,
10738                     feedback
10739                 ] 
10740             };  
10741         }
10742         
10743         if (this.before || this.after) {
10744             
10745             inputblock = {
10746                 cls : 'input-group',
10747                 cn :  [] 
10748             };
10749             
10750             if (this.before && typeof(this.before) == 'string') {
10751                 
10752                 inputblock.cn.push({
10753                     tag :'span',
10754                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10755                     html : this.before
10756                 });
10757             }
10758             if (this.before && typeof(this.before) == 'object') {
10759                 this.before = Roo.factory(this.before);
10760                 
10761                 inputblock.cn.push({
10762                     tag :'span',
10763                     cls : 'roo-input-before input-group-prepend   input-group-' +
10764                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10765                 });
10766             }
10767             
10768             inputblock.cn.push(input);
10769             
10770             if (this.after && typeof(this.after) == 'string') {
10771                 inputblock.cn.push({
10772                     tag :'span',
10773                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10774                     html : this.after
10775                 });
10776             }
10777             if (this.after && typeof(this.after) == 'object') {
10778                 this.after = Roo.factory(this.after);
10779                 
10780                 inputblock.cn.push({
10781                     tag :'span',
10782                     cls : 'roo-input-after input-group-append  input-group-' +
10783                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10784                 });
10785             }
10786             
10787             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10788                 inputblock.cls += ' has-feedback';
10789                 inputblock.cn.push(feedback);
10790             }
10791         };
10792         var indicator = {
10793             tag : 'i',
10794             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10795             tooltip : 'This field is required'
10796         };
10797         if (this.allowBlank ) {
10798             indicator.style = this.allowBlank ? ' display:none' : '';
10799         }
10800         if (align ==='left' && this.fieldLabel.length) {
10801             
10802             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10803             
10804             cfg.cn = [
10805                 indicator,
10806                 {
10807                     tag: 'label',
10808                     'for' :  id,
10809                     cls : 'control-label col-form-label',
10810                     html : this.fieldLabel
10811
10812                 },
10813                 {
10814                     cls : "", 
10815                     cn: [
10816                         inputblock
10817                     ]
10818                 }
10819             ];
10820             
10821             var labelCfg = cfg.cn[1];
10822             var contentCfg = cfg.cn[2];
10823             
10824             if(this.indicatorpos == 'right'){
10825                 cfg.cn = [
10826                     {
10827                         tag: 'label',
10828                         'for' :  id,
10829                         cls : 'control-label col-form-label',
10830                         cn : [
10831                             {
10832                                 tag : 'span',
10833                                 html : this.fieldLabel
10834                             },
10835                             indicator
10836                         ]
10837                     },
10838                     {
10839                         cls : "",
10840                         cn: [
10841                             inputblock
10842                         ]
10843                     }
10844
10845                 ];
10846                 
10847                 labelCfg = cfg.cn[0];
10848                 contentCfg = cfg.cn[1];
10849             
10850             }
10851             
10852             if(this.labelWidth > 12){
10853                 labelCfg.style = "width: " + this.labelWidth + 'px';
10854             }
10855             
10856             if(this.labelWidth < 13 && this.labelmd == 0){
10857                 this.labelmd = this.labelWidth;
10858             }
10859             
10860             if(this.labellg > 0){
10861                 labelCfg.cls += ' col-lg-' + this.labellg;
10862                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10863             }
10864             
10865             if(this.labelmd > 0){
10866                 labelCfg.cls += ' col-md-' + this.labelmd;
10867                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10868             }
10869             
10870             if(this.labelsm > 0){
10871                 labelCfg.cls += ' col-sm-' + this.labelsm;
10872                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10873             }
10874             
10875             if(this.labelxs > 0){
10876                 labelCfg.cls += ' col-xs-' + this.labelxs;
10877                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10878             }
10879             
10880             
10881         } else if ( this.fieldLabel.length) {
10882                 
10883             
10884             
10885             cfg.cn = [
10886                 {
10887                     tag : 'i',
10888                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10889                     tooltip : 'This field is required',
10890                     style : this.allowBlank ? ' display:none' : '' 
10891                 },
10892                 {
10893                     tag: 'label',
10894                    //cls : 'input-group-addon',
10895                     html : this.fieldLabel
10896
10897                 },
10898
10899                inputblock
10900
10901            ];
10902            
10903            if(this.indicatorpos == 'right'){
10904        
10905                 cfg.cn = [
10906                     {
10907                         tag: 'label',
10908                        //cls : 'input-group-addon',
10909                         html : this.fieldLabel
10910
10911                     },
10912                     {
10913                         tag : 'i',
10914                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10915                         tooltip : 'This field is required',
10916                         style : this.allowBlank ? ' display:none' : '' 
10917                     },
10918
10919                    inputblock
10920
10921                ];
10922
10923             }
10924
10925         } else {
10926             
10927             cfg.cn = [
10928
10929                     inputblock
10930
10931             ];
10932                 
10933                 
10934         };
10935         
10936         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10937            cfg.cls += ' navbar-form';
10938         }
10939         
10940         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10941             // on BS4 we do this only if not form 
10942             cfg.cls += ' navbar-form';
10943             cfg.tag = 'li';
10944         }
10945         
10946         return cfg;
10947         
10948     },
10949     /**
10950      * return the real input element.
10951      */
10952     inputEl: function ()
10953     {
10954         return this.el.select('input.form-control',true).first();
10955     },
10956     
10957     tooltipEl : function()
10958     {
10959         return this.inputEl();
10960     },
10961     
10962     indicatorEl : function()
10963     {
10964         if (Roo.bootstrap.version == 4) {
10965             return false; // not enabled in v4 yet.
10966         }
10967         
10968         var indicator = this.el.select('i.roo-required-indicator',true).first();
10969         
10970         if(!indicator){
10971             return false;
10972         }
10973         
10974         return indicator;
10975         
10976     },
10977     
10978     setDisabled : function(v)
10979     {
10980         var i  = this.inputEl().dom;
10981         if (!v) {
10982             i.removeAttribute('disabled');
10983             return;
10984             
10985         }
10986         i.setAttribute('disabled','true');
10987     },
10988     initEvents : function()
10989     {
10990           
10991         this.inputEl().on("keydown" , this.fireKey,  this);
10992         this.inputEl().on("focus", this.onFocus,  this);
10993         this.inputEl().on("blur", this.onBlur,  this);
10994         
10995         this.inputEl().relayEvent('keyup', this);
10996         
10997         this.indicator = this.indicatorEl();
10998         
10999         if(this.indicator){
11000             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11001         }
11002  
11003         // reference to original value for reset
11004         this.originalValue = this.getValue();
11005         //Roo.form.TextField.superclass.initEvents.call(this);
11006         if(this.validationEvent == 'keyup'){
11007             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11008             this.inputEl().on('keyup', this.filterValidation, this);
11009         }
11010         else if(this.validationEvent !== false){
11011             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11012         }
11013         
11014         if(this.selectOnFocus){
11015             this.on("focus", this.preFocus, this);
11016             
11017         }
11018         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11019             this.inputEl().on("keypress", this.filterKeys, this);
11020         } else {
11021             this.inputEl().relayEvent('keypress', this);
11022         }
11023        /* if(this.grow){
11024             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11025             this.el.on("click", this.autoSize,  this);
11026         }
11027         */
11028         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11029             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11030         }
11031         
11032         if (typeof(this.before) == 'object') {
11033             this.before.render(this.el.select('.roo-input-before',true).first());
11034         }
11035         if (typeof(this.after) == 'object') {
11036             this.after.render(this.el.select('.roo-input-after',true).first());
11037         }
11038         
11039         this.inputEl().on('change', this.onChange, this);
11040         
11041     },
11042     filterValidation : function(e){
11043         if(!e.isNavKeyPress()){
11044             this.validationTask.delay(this.validationDelay);
11045         }
11046     },
11047      /**
11048      * Validates the field value
11049      * @return {Boolean} True if the value is valid, else false
11050      */
11051     validate : function(){
11052         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11053         if(this.disabled || this.validateValue(this.getRawValue())){
11054             this.markValid();
11055             return true;
11056         }
11057         
11058         this.markInvalid();
11059         return false;
11060     },
11061     
11062     
11063     /**
11064      * Validates a value according to the field's validation rules and marks the field as invalid
11065      * if the validation fails
11066      * @param {Mixed} value The value to validate
11067      * @return {Boolean} True if the value is valid, else false
11068      */
11069     validateValue : function(value)
11070     {
11071         if(this.getVisibilityEl().hasClass('hidden')){
11072             return true;
11073         }
11074         
11075         if(value.length < 1)  { // if it's blank
11076             if(this.allowBlank){
11077                 return true;
11078             }
11079             return false;
11080         }
11081         
11082         if(value.length < this.minLength){
11083             return false;
11084         }
11085         if(value.length > this.maxLength){
11086             return false;
11087         }
11088         if(this.vtype){
11089             var vt = Roo.form.VTypes;
11090             if(!vt[this.vtype](value, this)){
11091                 return false;
11092             }
11093         }
11094         if(typeof this.validator == "function"){
11095             var msg = this.validator(value);
11096             if(msg !== true){
11097                 return false;
11098             }
11099             if (typeof(msg) == 'string') {
11100                 this.invalidText = msg;
11101             }
11102         }
11103         
11104         if(this.regex && !this.regex.test(value)){
11105             return false;
11106         }
11107         
11108         return true;
11109     },
11110     
11111      // private
11112     fireKey : function(e){
11113         //Roo.log('field ' + e.getKey());
11114         if(e.isNavKeyPress()){
11115             this.fireEvent("specialkey", this, e);
11116         }
11117     },
11118     focus : function (selectText){
11119         if(this.rendered){
11120             this.inputEl().focus();
11121             if(selectText === true){
11122                 this.inputEl().dom.select();
11123             }
11124         }
11125         return this;
11126     } ,
11127     
11128     onFocus : function(){
11129         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11130            // this.el.addClass(this.focusClass);
11131         }
11132         if(!this.hasFocus){
11133             this.hasFocus = true;
11134             this.startValue = this.getValue();
11135             this.fireEvent("focus", this);
11136         }
11137     },
11138     
11139     beforeBlur : Roo.emptyFn,
11140
11141     
11142     // private
11143     onBlur : function(){
11144         this.beforeBlur();
11145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11146             //this.el.removeClass(this.focusClass);
11147         }
11148         this.hasFocus = false;
11149         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11150             this.validate();
11151         }
11152         var v = this.getValue();
11153         if(String(v) !== String(this.startValue)){
11154             this.fireEvent('change', this, v, this.startValue);
11155         }
11156         this.fireEvent("blur", this);
11157     },
11158     
11159     onChange : function(e)
11160     {
11161         var v = this.getValue();
11162         if(String(v) !== String(this.startValue)){
11163             this.fireEvent('change', this, v, this.startValue);
11164         }
11165         
11166     },
11167     
11168     /**
11169      * Resets the current field value to the originally loaded value and clears any validation messages
11170      */
11171     reset : function(){
11172         this.setValue(this.originalValue);
11173         this.validate();
11174     },
11175      /**
11176      * Returns the name of the field
11177      * @return {Mixed} name The name field
11178      */
11179     getName: function(){
11180         return this.name;
11181     },
11182      /**
11183      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11184      * @return {Mixed} value The field value
11185      */
11186     getValue : function(){
11187         
11188         var v = this.inputEl().getValue();
11189         
11190         return v;
11191     },
11192     /**
11193      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11194      * @return {Mixed} value The field value
11195      */
11196     getRawValue : function(){
11197         var v = this.inputEl().getValue();
11198         
11199         return v;
11200     },
11201     
11202     /**
11203      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11204      * @param {Mixed} value The value to set
11205      */
11206     setRawValue : function(v){
11207         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11208     },
11209     
11210     selectText : function(start, end){
11211         var v = this.getRawValue();
11212         if(v.length > 0){
11213             start = start === undefined ? 0 : start;
11214             end = end === undefined ? v.length : end;
11215             var d = this.inputEl().dom;
11216             if(d.setSelectionRange){
11217                 d.setSelectionRange(start, end);
11218             }else if(d.createTextRange){
11219                 var range = d.createTextRange();
11220                 range.moveStart("character", start);
11221                 range.moveEnd("character", v.length-end);
11222                 range.select();
11223             }
11224         }
11225     },
11226     
11227     /**
11228      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11229      * @param {Mixed} value The value to set
11230      */
11231     setValue : function(v){
11232         this.value = v;
11233         if(this.rendered){
11234             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11235             this.validate();
11236         }
11237     },
11238     
11239     /*
11240     processValue : function(value){
11241         if(this.stripCharsRe){
11242             var newValue = value.replace(this.stripCharsRe, '');
11243             if(newValue !== value){
11244                 this.setRawValue(newValue);
11245                 return newValue;
11246             }
11247         }
11248         return value;
11249     },
11250   */
11251     preFocus : function(){
11252         
11253         if(this.selectOnFocus){
11254             this.inputEl().dom.select();
11255         }
11256     },
11257     filterKeys : function(e){
11258         var k = e.getKey();
11259         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11260             return;
11261         }
11262         var c = e.getCharCode(), cc = String.fromCharCode(c);
11263         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11264             return;
11265         }
11266         if(!this.maskRe.test(cc)){
11267             e.stopEvent();
11268         }
11269     },
11270      /**
11271      * Clear any invalid styles/messages for this field
11272      */
11273     clearInvalid : function(){
11274         
11275         if(!this.el || this.preventMark){ // not rendered
11276             return;
11277         }
11278         
11279         
11280         this.el.removeClass([this.invalidClass, 'is-invalid']);
11281         
11282         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11283             
11284             var feedback = this.el.select('.form-control-feedback', true).first();
11285             
11286             if(feedback){
11287                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11288             }
11289             
11290         }
11291         
11292         if(this.indicator){
11293             this.indicator.removeClass('visible');
11294             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11295         }
11296         
11297         this.fireEvent('valid', this);
11298     },
11299     
11300      /**
11301      * Mark this field as valid
11302      */
11303     markValid : function()
11304     {
11305         if(!this.el  || this.preventMark){ // not rendered...
11306             return;
11307         }
11308         
11309         this.el.removeClass([this.invalidClass, this.validClass]);
11310         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11311
11312         var feedback = this.el.select('.form-control-feedback', true).first();
11313             
11314         if(feedback){
11315             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11316         }
11317         
11318         if(this.indicator){
11319             this.indicator.removeClass('visible');
11320             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11321         }
11322         
11323         if(this.disabled){
11324             return;
11325         }
11326         
11327            
11328         if(this.allowBlank && !this.getRawValue().length){
11329             return;
11330         }
11331         if (Roo.bootstrap.version == 3) {
11332             this.el.addClass(this.validClass);
11333         } else {
11334             this.inputEl().addClass('is-valid');
11335         }
11336
11337         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11338             
11339             var feedback = this.el.select('.form-control-feedback', true).first();
11340             
11341             if(feedback){
11342                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11343                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11344             }
11345             
11346         }
11347         
11348         this.fireEvent('valid', this);
11349     },
11350     
11351      /**
11352      * Mark this field as invalid
11353      * @param {String} msg The validation message
11354      */
11355     markInvalid : function(msg)
11356     {
11357         if(!this.el  || this.preventMark){ // not rendered
11358             return;
11359         }
11360         
11361         this.el.removeClass([this.invalidClass, this.validClass]);
11362         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11363         
11364         var feedback = this.el.select('.form-control-feedback', true).first();
11365             
11366         if(feedback){
11367             this.el.select('.form-control-feedback', true).first().removeClass(
11368                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11369         }
11370
11371         if(this.disabled){
11372             return;
11373         }
11374         
11375         if(this.allowBlank && !this.getRawValue().length){
11376             return;
11377         }
11378         
11379         if(this.indicator){
11380             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11381             this.indicator.addClass('visible');
11382         }
11383         if (Roo.bootstrap.version == 3) {
11384             this.el.addClass(this.invalidClass);
11385         } else {
11386             this.inputEl().addClass('is-invalid');
11387         }
11388         
11389         
11390         
11391         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11392             
11393             var feedback = this.el.select('.form-control-feedback', true).first();
11394             
11395             if(feedback){
11396                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11397                 
11398                 if(this.getValue().length || this.forceFeedback){
11399                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11400                 }
11401                 
11402             }
11403             
11404         }
11405         
11406         this.fireEvent('invalid', this, msg);
11407     },
11408     // private
11409     SafariOnKeyDown : function(event)
11410     {
11411         // this is a workaround for a password hang bug on chrome/ webkit.
11412         if (this.inputEl().dom.type != 'password') {
11413             return;
11414         }
11415         
11416         var isSelectAll = false;
11417         
11418         if(this.inputEl().dom.selectionEnd > 0){
11419             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11420         }
11421         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11422             event.preventDefault();
11423             this.setValue('');
11424             return;
11425         }
11426         
11427         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11428             
11429             event.preventDefault();
11430             // this is very hacky as keydown always get's upper case.
11431             //
11432             var cc = String.fromCharCode(event.getCharCode());
11433             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11434             
11435         }
11436     },
11437     adjustWidth : function(tag, w){
11438         tag = tag.toLowerCase();
11439         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11440             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11441                 if(tag == 'input'){
11442                     return w + 2;
11443                 }
11444                 if(tag == 'textarea'){
11445                     return w-2;
11446                 }
11447             }else if(Roo.isOpera){
11448                 if(tag == 'input'){
11449                     return w + 2;
11450                 }
11451                 if(tag == 'textarea'){
11452                     return w-2;
11453                 }
11454             }
11455         }
11456         return w;
11457     },
11458     
11459     setFieldLabel : function(v)
11460     {
11461         if(!this.rendered){
11462             return;
11463         }
11464         
11465         if(this.indicatorEl()){
11466             var ar = this.el.select('label > span',true);
11467             
11468             if (ar.elements.length) {
11469                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11470                 this.fieldLabel = v;
11471                 return;
11472             }
11473             
11474             var br = this.el.select('label',true);
11475             
11476             if(br.elements.length) {
11477                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11478                 this.fieldLabel = v;
11479                 return;
11480             }
11481             
11482             Roo.log('Cannot Found any of label > span || label in input');
11483             return;
11484         }
11485         
11486         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11487         this.fieldLabel = v;
11488         
11489         
11490     }
11491 });
11492
11493  
11494 /*
11495  * - LGPL
11496  *
11497  * Input
11498  * 
11499  */
11500
11501 /**
11502  * @class Roo.bootstrap.TextArea
11503  * @extends Roo.bootstrap.Input
11504  * Bootstrap TextArea class
11505  * @cfg {Number} cols Specifies the visible width of a text area
11506  * @cfg {Number} rows Specifies the visible number of lines in a text area
11507  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11508  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11509  * @cfg {string} html text
11510  * 
11511  * @constructor
11512  * Create a new TextArea
11513  * @param {Object} config The config object
11514  */
11515
11516 Roo.bootstrap.TextArea = function(config){
11517     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11518    
11519 };
11520
11521 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11522      
11523     cols : false,
11524     rows : 5,
11525     readOnly : false,
11526     warp : 'soft',
11527     resize : false,
11528     value: false,
11529     html: false,
11530     
11531     getAutoCreate : function(){
11532         
11533         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11534         
11535         var id = Roo.id();
11536         
11537         var cfg = {};
11538         
11539         if(this.inputType != 'hidden'){
11540             cfg.cls = 'form-group' //input-group
11541         }
11542         
11543         var input =  {
11544             tag: 'textarea',
11545             id : id,
11546             warp : this.warp,
11547             rows : this.rows,
11548             value : this.value || '',
11549             html: this.html || '',
11550             cls : 'form-control',
11551             placeholder : this.placeholder || '' 
11552             
11553         };
11554         
11555         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11556             input.maxLength = this.maxLength;
11557         }
11558         
11559         if(this.resize){
11560             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11561         }
11562         
11563         if(this.cols){
11564             input.cols = this.cols;
11565         }
11566         
11567         if (this.readOnly) {
11568             input.readonly = true;
11569         }
11570         
11571         if (this.name) {
11572             input.name = this.name;
11573         }
11574         
11575         if (this.size) {
11576             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11577         }
11578         
11579         var settings=this;
11580         ['xs','sm','md','lg'].map(function(size){
11581             if (settings[size]) {
11582                 cfg.cls += ' col-' + size + '-' + settings[size];
11583             }
11584         });
11585         
11586         var inputblock = input;
11587         
11588         if(this.hasFeedback && !this.allowBlank){
11589             
11590             var feedback = {
11591                 tag: 'span',
11592                 cls: 'glyphicon form-control-feedback'
11593             };
11594
11595             inputblock = {
11596                 cls : 'has-feedback',
11597                 cn :  [
11598                     input,
11599                     feedback
11600                 ] 
11601             };  
11602         }
11603         
11604         
11605         if (this.before || this.after) {
11606             
11607             inputblock = {
11608                 cls : 'input-group',
11609                 cn :  [] 
11610             };
11611             if (this.before) {
11612                 inputblock.cn.push({
11613                     tag :'span',
11614                     cls : 'input-group-addon',
11615                     html : this.before
11616                 });
11617             }
11618             
11619             inputblock.cn.push(input);
11620             
11621             if(this.hasFeedback && !this.allowBlank){
11622                 inputblock.cls += ' has-feedback';
11623                 inputblock.cn.push(feedback);
11624             }
11625             
11626             if (this.after) {
11627                 inputblock.cn.push({
11628                     tag :'span',
11629                     cls : 'input-group-addon',
11630                     html : this.after
11631                 });
11632             }
11633             
11634         }
11635         
11636         if (align ==='left' && this.fieldLabel.length) {
11637             cfg.cn = [
11638                 {
11639                     tag: 'label',
11640                     'for' :  id,
11641                     cls : 'control-label',
11642                     html : this.fieldLabel
11643                 },
11644                 {
11645                     cls : "",
11646                     cn: [
11647                         inputblock
11648                     ]
11649                 }
11650
11651             ];
11652             
11653             if(this.labelWidth > 12){
11654                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11655             }
11656
11657             if(this.labelWidth < 13 && this.labelmd == 0){
11658                 this.labelmd = this.labelWidth;
11659             }
11660
11661             if(this.labellg > 0){
11662                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11663                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11664             }
11665
11666             if(this.labelmd > 0){
11667                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11668                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11669             }
11670
11671             if(this.labelsm > 0){
11672                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11673                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11674             }
11675
11676             if(this.labelxs > 0){
11677                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11678                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11679             }
11680             
11681         } else if ( this.fieldLabel.length) {
11682             cfg.cn = [
11683
11684                {
11685                    tag: 'label',
11686                    //cls : 'input-group-addon',
11687                    html : this.fieldLabel
11688
11689                },
11690
11691                inputblock
11692
11693            ];
11694
11695         } else {
11696
11697             cfg.cn = [
11698
11699                 inputblock
11700
11701             ];
11702                 
11703         }
11704         
11705         if (this.disabled) {
11706             input.disabled=true;
11707         }
11708         
11709         return cfg;
11710         
11711     },
11712     /**
11713      * return the real textarea element.
11714      */
11715     inputEl: function ()
11716     {
11717         return this.el.select('textarea.form-control',true).first();
11718     },
11719     
11720     /**
11721      * Clear any invalid styles/messages for this field
11722      */
11723     clearInvalid : function()
11724     {
11725         
11726         if(!this.el || this.preventMark){ // not rendered
11727             return;
11728         }
11729         
11730         var label = this.el.select('label', true).first();
11731         var icon = this.el.select('i.fa-star', true).first();
11732         
11733         if(label && icon){
11734             icon.remove();
11735         }
11736         this.el.removeClass( this.validClass);
11737         this.inputEl().removeClass('is-invalid');
11738          
11739         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11740             
11741             var feedback = this.el.select('.form-control-feedback', true).first();
11742             
11743             if(feedback){
11744                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11745             }
11746             
11747         }
11748         
11749         this.fireEvent('valid', this);
11750     },
11751     
11752      /**
11753      * Mark this field as valid
11754      */
11755     markValid : function()
11756     {
11757         if(!this.el  || this.preventMark){ // not rendered
11758             return;
11759         }
11760         
11761         this.el.removeClass([this.invalidClass, this.validClass]);
11762         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11763         
11764         var feedback = this.el.select('.form-control-feedback', true).first();
11765             
11766         if(feedback){
11767             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11768         }
11769
11770         if(this.disabled || this.allowBlank){
11771             return;
11772         }
11773         
11774         var label = this.el.select('label', true).first();
11775         var icon = this.el.select('i.fa-star', true).first();
11776         
11777         if(label && icon){
11778             icon.remove();
11779         }
11780         if (Roo.bootstrap.version == 3) {
11781             this.el.addClass(this.validClass);
11782         } else {
11783             this.inputEl().addClass('is-valid');
11784         }
11785         
11786         
11787         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11788             
11789             var feedback = this.el.select('.form-control-feedback', true).first();
11790             
11791             if(feedback){
11792                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11793                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11794             }
11795             
11796         }
11797         
11798         this.fireEvent('valid', this);
11799     },
11800     
11801      /**
11802      * Mark this field as invalid
11803      * @param {String} msg The validation message
11804      */
11805     markInvalid : function(msg)
11806     {
11807         if(!this.el  || this.preventMark){ // not rendered
11808             return;
11809         }
11810         
11811         this.el.removeClass([this.invalidClass, this.validClass]);
11812         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11813         
11814         var feedback = this.el.select('.form-control-feedback', true).first();
11815             
11816         if(feedback){
11817             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11818         }
11819
11820         if(this.disabled || this.allowBlank){
11821             return;
11822         }
11823         
11824         var label = this.el.select('label', true).first();
11825         var icon = this.el.select('i.fa-star', true).first();
11826         
11827         if(!this.getValue().length && label && !icon){
11828             this.el.createChild({
11829                 tag : 'i',
11830                 cls : 'text-danger fa fa-lg fa-star',
11831                 tooltip : 'This field is required',
11832                 style : 'margin-right:5px;'
11833             }, label, true);
11834         }
11835         
11836         if (Roo.bootstrap.version == 3) {
11837             this.el.addClass(this.invalidClass);
11838         } else {
11839             this.inputEl().addClass('is-invalid');
11840         }
11841         
11842         // fixme ... this may be depricated need to test..
11843         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11844             
11845             var feedback = this.el.select('.form-control-feedback', true).first();
11846             
11847             if(feedback){
11848                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11849                 
11850                 if(this.getValue().length || this.forceFeedback){
11851                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11852                 }
11853                 
11854             }
11855             
11856         }
11857         
11858         this.fireEvent('invalid', this, msg);
11859     }
11860 });
11861
11862  
11863 /*
11864  * - LGPL
11865  *
11866  * trigger field - base class for combo..
11867  * 
11868  */
11869  
11870 /**
11871  * @class Roo.bootstrap.TriggerField
11872  * @extends Roo.bootstrap.Input
11873  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11874  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11875  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11876  * for which you can provide a custom implementation.  For example:
11877  * <pre><code>
11878 var trigger = new Roo.bootstrap.TriggerField();
11879 trigger.onTriggerClick = myTriggerFn;
11880 trigger.applyTo('my-field');
11881 </code></pre>
11882  *
11883  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11884  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11885  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11886  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11887  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11888
11889  * @constructor
11890  * Create a new TriggerField.
11891  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11892  * to the base TextField)
11893  */
11894 Roo.bootstrap.TriggerField = function(config){
11895     this.mimicing = false;
11896     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11897 };
11898
11899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11900     /**
11901      * @cfg {String} triggerClass A CSS class to apply to the trigger
11902      */
11903      /**
11904      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11905      */
11906     hideTrigger:false,
11907
11908     /**
11909      * @cfg {Boolean} removable (true|false) special filter default false
11910      */
11911     removable : false,
11912     
11913     /** @cfg {Boolean} grow @hide */
11914     /** @cfg {Number} growMin @hide */
11915     /** @cfg {Number} growMax @hide */
11916
11917     /**
11918      * @hide 
11919      * @method
11920      */
11921     autoSize: Roo.emptyFn,
11922     // private
11923     monitorTab : true,
11924     // private
11925     deferHeight : true,
11926
11927     
11928     actionMode : 'wrap',
11929     
11930     caret : false,
11931     
11932     
11933     getAutoCreate : function(){
11934        
11935         var align = this.labelAlign || this.parentLabelAlign();
11936         
11937         var id = Roo.id();
11938         
11939         var cfg = {
11940             cls: 'form-group' //input-group
11941         };
11942         
11943         
11944         var input =  {
11945             tag: 'input',
11946             id : id,
11947             type : this.inputType,
11948             cls : 'form-control',
11949             autocomplete: 'new-password',
11950             placeholder : this.placeholder || '' 
11951             
11952         };
11953         if (this.name) {
11954             input.name = this.name;
11955         }
11956         if (this.size) {
11957             input.cls += ' input-' + this.size;
11958         }
11959         
11960         if (this.disabled) {
11961             input.disabled=true;
11962         }
11963         
11964         var inputblock = input;
11965         
11966         if(this.hasFeedback && !this.allowBlank){
11967             
11968             var feedback = {
11969                 tag: 'span',
11970                 cls: 'glyphicon form-control-feedback'
11971             };
11972             
11973             if(this.removable && !this.editable  ){
11974                 inputblock = {
11975                     cls : 'has-feedback',
11976                     cn :  [
11977                         inputblock,
11978                         {
11979                             tag: 'button',
11980                             html : 'x',
11981                             cls : 'roo-combo-removable-btn close'
11982                         },
11983                         feedback
11984                     ] 
11985                 };
11986             } else {
11987                 inputblock = {
11988                     cls : 'has-feedback',
11989                     cn :  [
11990                         inputblock,
11991                         feedback
11992                     ] 
11993                 };
11994             }
11995
11996         } else {
11997             if(this.removable && !this.editable ){
11998                 inputblock = {
11999                     cls : 'roo-removable',
12000                     cn :  [
12001                         inputblock,
12002                         {
12003                             tag: 'button',
12004                             html : 'x',
12005                             cls : 'roo-combo-removable-btn close'
12006                         }
12007                     ] 
12008                 };
12009             }
12010         }
12011         
12012         if (this.before || this.after) {
12013             
12014             inputblock = {
12015                 cls : 'input-group',
12016                 cn :  [] 
12017             };
12018             if (this.before) {
12019                 inputblock.cn.push({
12020                     tag :'span',
12021                     cls : 'input-group-addon input-group-prepend input-group-text',
12022                     html : this.before
12023                 });
12024             }
12025             
12026             inputblock.cn.push(input);
12027             
12028             if(this.hasFeedback && !this.allowBlank){
12029                 inputblock.cls += ' has-feedback';
12030                 inputblock.cn.push(feedback);
12031             }
12032             
12033             if (this.after) {
12034                 inputblock.cn.push({
12035                     tag :'span',
12036                     cls : 'input-group-addon input-group-append input-group-text',
12037                     html : this.after
12038                 });
12039             }
12040             
12041         };
12042         
12043       
12044         
12045         var ibwrap = inputblock;
12046         
12047         if(this.multiple){
12048             ibwrap = {
12049                 tag: 'ul',
12050                 cls: 'roo-select2-choices',
12051                 cn:[
12052                     {
12053                         tag: 'li',
12054                         cls: 'roo-select2-search-field',
12055                         cn: [
12056
12057                             inputblock
12058                         ]
12059                     }
12060                 ]
12061             };
12062                 
12063         }
12064         
12065         var combobox = {
12066             cls: 'roo-select2-container input-group',
12067             cn: [
12068                  {
12069                     tag: 'input',
12070                     type : 'hidden',
12071                     cls: 'form-hidden-field'
12072                 },
12073                 ibwrap
12074             ]
12075         };
12076         
12077         if(!this.multiple && this.showToggleBtn){
12078             
12079             var caret = {
12080                         tag: 'span',
12081                         cls: 'caret'
12082              };
12083             if (this.caret != false) {
12084                 caret = {
12085                      tag: 'i',
12086                      cls: 'fa fa-' + this.caret
12087                 };
12088                 
12089             }
12090             
12091             combobox.cn.push({
12092                 tag :'span',
12093                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12094                 cn : [
12095                     Roo.bootstrap.version == 3 ? caret : '',
12096                     {
12097                         tag: 'span',
12098                         cls: 'combobox-clear',
12099                         cn  : [
12100                             {
12101                                 tag : 'i',
12102                                 cls: 'icon-remove'
12103                             }
12104                         ]
12105                     }
12106                 ]
12107
12108             })
12109         }
12110         
12111         if(this.multiple){
12112             combobox.cls += ' roo-select2-container-multi';
12113         }
12114          var indicator = {
12115             tag : 'i',
12116             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12117             tooltip : 'This field is required'
12118         };
12119         if (Roo.bootstrap.version == 4) {
12120             indicator = {
12121                 tag : 'i',
12122                 style : 'display:none'
12123             };
12124         }
12125         
12126         
12127         if (align ==='left' && this.fieldLabel.length) {
12128             
12129             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12130
12131             cfg.cn = [
12132                 indicator,
12133                 {
12134                     tag: 'label',
12135                     'for' :  id,
12136                     cls : 'control-label',
12137                     html : this.fieldLabel
12138
12139                 },
12140                 {
12141                     cls : "", 
12142                     cn: [
12143                         combobox
12144                     ]
12145                 }
12146
12147             ];
12148             
12149             var labelCfg = cfg.cn[1];
12150             var contentCfg = cfg.cn[2];
12151             
12152             if(this.indicatorpos == 'right'){
12153                 cfg.cn = [
12154                     {
12155                         tag: 'label',
12156                         'for' :  id,
12157                         cls : 'control-label',
12158                         cn : [
12159                             {
12160                                 tag : 'span',
12161                                 html : this.fieldLabel
12162                             },
12163                             indicator
12164                         ]
12165                     },
12166                     {
12167                         cls : "", 
12168                         cn: [
12169                             combobox
12170                         ]
12171                     }
12172
12173                 ];
12174                 
12175                 labelCfg = cfg.cn[0];
12176                 contentCfg = cfg.cn[1];
12177             }
12178             
12179             if(this.labelWidth > 12){
12180                 labelCfg.style = "width: " + this.labelWidth + 'px';
12181             }
12182             
12183             if(this.labelWidth < 13 && this.labelmd == 0){
12184                 this.labelmd = this.labelWidth;
12185             }
12186             
12187             if(this.labellg > 0){
12188                 labelCfg.cls += ' col-lg-' + this.labellg;
12189                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12190             }
12191             
12192             if(this.labelmd > 0){
12193                 labelCfg.cls += ' col-md-' + this.labelmd;
12194                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12195             }
12196             
12197             if(this.labelsm > 0){
12198                 labelCfg.cls += ' col-sm-' + this.labelsm;
12199                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12200             }
12201             
12202             if(this.labelxs > 0){
12203                 labelCfg.cls += ' col-xs-' + this.labelxs;
12204                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12205             }
12206             
12207         } else if ( this.fieldLabel.length) {
12208 //                Roo.log(" label");
12209             cfg.cn = [
12210                 indicator,
12211                {
12212                    tag: 'label',
12213                    //cls : 'input-group-addon',
12214                    html : this.fieldLabel
12215
12216                },
12217
12218                combobox
12219
12220             ];
12221             
12222             if(this.indicatorpos == 'right'){
12223                 
12224                 cfg.cn = [
12225                     {
12226                        tag: 'label',
12227                        cn : [
12228                            {
12229                                tag : 'span',
12230                                html : this.fieldLabel
12231                            },
12232                            indicator
12233                        ]
12234
12235                     },
12236                     combobox
12237
12238                 ];
12239
12240             }
12241
12242         } else {
12243             
12244 //                Roo.log(" no label && no align");
12245                 cfg = combobox
12246                      
12247                 
12248         }
12249         
12250         var settings=this;
12251         ['xs','sm','md','lg'].map(function(size){
12252             if (settings[size]) {
12253                 cfg.cls += ' col-' + size + '-' + settings[size];
12254             }
12255         });
12256         
12257         return cfg;
12258         
12259     },
12260     
12261     
12262     
12263     // private
12264     onResize : function(w, h){
12265 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12266 //        if(typeof w == 'number'){
12267 //            var x = w - this.trigger.getWidth();
12268 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12269 //            this.trigger.setStyle('left', x+'px');
12270 //        }
12271     },
12272
12273     // private
12274     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12275
12276     // private
12277     getResizeEl : function(){
12278         return this.inputEl();
12279     },
12280
12281     // private
12282     getPositionEl : function(){
12283         return this.inputEl();
12284     },
12285
12286     // private
12287     alignErrorIcon : function(){
12288         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12289     },
12290
12291     // private
12292     initEvents : function(){
12293         
12294         this.createList();
12295         
12296         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12297         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12298         if(!this.multiple && this.showToggleBtn){
12299             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12300             if(this.hideTrigger){
12301                 this.trigger.setDisplayed(false);
12302             }
12303             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12304         }
12305         
12306         if(this.multiple){
12307             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12308         }
12309         
12310         if(this.removable && !this.editable && !this.tickable){
12311             var close = this.closeTriggerEl();
12312             
12313             if(close){
12314                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12315                 close.on('click', this.removeBtnClick, this, close);
12316             }
12317         }
12318         
12319         //this.trigger.addClassOnOver('x-form-trigger-over');
12320         //this.trigger.addClassOnClick('x-form-trigger-click');
12321         
12322         //if(!this.width){
12323         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12324         //}
12325     },
12326     
12327     closeTriggerEl : function()
12328     {
12329         var close = this.el.select('.roo-combo-removable-btn', true).first();
12330         return close ? close : false;
12331     },
12332     
12333     removeBtnClick : function(e, h, el)
12334     {
12335         e.preventDefault();
12336         
12337         if(this.fireEvent("remove", this) !== false){
12338             this.reset();
12339             this.fireEvent("afterremove", this)
12340         }
12341     },
12342     
12343     createList : function()
12344     {
12345         this.list = Roo.get(document.body).createChild({
12346             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12347             cls: 'typeahead typeahead-long dropdown-menu shadow',
12348             style: 'display:none'
12349         });
12350         
12351         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12352         
12353     },
12354
12355     // private
12356     initTrigger : function(){
12357        
12358     },
12359
12360     // private
12361     onDestroy : function(){
12362         if(this.trigger){
12363             this.trigger.removeAllListeners();
12364           //  this.trigger.remove();
12365         }
12366         //if(this.wrap){
12367         //    this.wrap.remove();
12368         //}
12369         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12370     },
12371
12372     // private
12373     onFocus : function(){
12374         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12375         /*
12376         if(!this.mimicing){
12377             this.wrap.addClass('x-trigger-wrap-focus');
12378             this.mimicing = true;
12379             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12380             if(this.monitorTab){
12381                 this.el.on("keydown", this.checkTab, this);
12382             }
12383         }
12384         */
12385     },
12386
12387     // private
12388     checkTab : function(e){
12389         if(e.getKey() == e.TAB){
12390             this.triggerBlur();
12391         }
12392     },
12393
12394     // private
12395     onBlur : function(){
12396         // do nothing
12397     },
12398
12399     // private
12400     mimicBlur : function(e, t){
12401         /*
12402         if(!this.wrap.contains(t) && this.validateBlur()){
12403             this.triggerBlur();
12404         }
12405         */
12406     },
12407
12408     // private
12409     triggerBlur : function(){
12410         this.mimicing = false;
12411         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12412         if(this.monitorTab){
12413             this.el.un("keydown", this.checkTab, this);
12414         }
12415         //this.wrap.removeClass('x-trigger-wrap-focus');
12416         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12417     },
12418
12419     // private
12420     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12421     validateBlur : function(e, t){
12422         return true;
12423     },
12424
12425     // private
12426     onDisable : function(){
12427         this.inputEl().dom.disabled = true;
12428         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12429         //if(this.wrap){
12430         //    this.wrap.addClass('x-item-disabled');
12431         //}
12432     },
12433
12434     // private
12435     onEnable : function(){
12436         this.inputEl().dom.disabled = false;
12437         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12438         //if(this.wrap){
12439         //    this.el.removeClass('x-item-disabled');
12440         //}
12441     },
12442
12443     // private
12444     onShow : function(){
12445         var ae = this.getActionEl();
12446         
12447         if(ae){
12448             ae.dom.style.display = '';
12449             ae.dom.style.visibility = 'visible';
12450         }
12451     },
12452
12453     // private
12454     
12455     onHide : function(){
12456         var ae = this.getActionEl();
12457         ae.dom.style.display = 'none';
12458     },
12459
12460     /**
12461      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12462      * by an implementing function.
12463      * @method
12464      * @param {EventObject} e
12465      */
12466     onTriggerClick : Roo.emptyFn
12467 });
12468  
12469 /*
12470 * Licence: LGPL
12471 */
12472
12473 /**
12474  * @class Roo.bootstrap.CardUploader
12475  * @extends Roo.bootstrap.Button
12476  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12477  * @cfg {Number} errorTimeout default 3000
12478  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12479  * @cfg {Array}  html The button text.
12480
12481  *
12482  * @constructor
12483  * Create a new CardUploader
12484  * @param {Object} config The config object
12485  */
12486
12487 Roo.bootstrap.CardUploader = function(config){
12488     
12489  
12490     
12491     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12492     
12493     
12494     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12495         return r.data.id
12496         });
12497     
12498     
12499 };
12500
12501 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12502     
12503      
12504     errorTimeout : 3000,
12505      
12506     images : false,
12507    
12508     fileCollection : false,
12509     allowBlank : true,
12510     
12511     getAutoCreate : function()
12512     {
12513         
12514         var cfg =  {
12515             cls :'form-group' ,
12516             cn : [
12517                
12518                 {
12519                     tag: 'label',
12520                    //cls : 'input-group-addon',
12521                     html : this.fieldLabel
12522
12523                 },
12524
12525                 {
12526                     tag: 'input',
12527                     type : 'hidden',
12528                     value : this.value,
12529                     cls : 'd-none  form-control'
12530                 },
12531                 
12532                 {
12533                     tag: 'input',
12534                     multiple : 'multiple',
12535                     type : 'file',
12536                     cls : 'd-none  roo-card-upload-selector'
12537                 },
12538                 
12539                 {
12540                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12541                 },
12542                 {
12543                     cls : 'card-columns roo-card-uploader-container'
12544                 }
12545
12546             ]
12547         };
12548            
12549          
12550         return cfg;
12551     },
12552     
12553     getChildContainer : function() /// what children are added to.
12554     {
12555         return this.containerEl;
12556     },
12557    
12558     getButtonContainer : function() /// what children are added to.
12559     {
12560         return this.el.select(".roo-card-uploader-button-container").first();
12561     },
12562    
12563     initEvents : function()
12564     {
12565         
12566         Roo.bootstrap.Input.prototype.initEvents.call(this);
12567         
12568         var t = this;
12569         this.addxtype({
12570             xns: Roo.bootstrap,
12571
12572             xtype : 'Button',
12573             container_method : 'getButtonContainer' ,            
12574             html :  this.html, // fix changable?
12575             cls : 'w-100 ',
12576             listeners : {
12577                 'click' : function(btn, e) {
12578                     t.onClick(e);
12579                 }
12580             }
12581         });
12582         
12583         
12584         
12585         
12586         this.urlAPI = (window.createObjectURL && window) || 
12587                                 (window.URL && URL.revokeObjectURL && URL) || 
12588                                 (window.webkitURL && webkitURL);
12589                         
12590          
12591          
12592          
12593         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12594         
12595         this.selectorEl.on('change', this.onFileSelected, this);
12596         if (this.images) {
12597             var t = this;
12598             this.images.forEach(function(img) {
12599                 t.addCard(img)
12600             });
12601             this.images = false;
12602         }
12603         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12604          
12605        
12606     },
12607     
12608    
12609     onClick : function(e)
12610     {
12611         e.preventDefault();
12612          
12613         this.selectorEl.dom.click();
12614          
12615     },
12616     
12617     onFileSelected : function(e)
12618     {
12619         e.preventDefault();
12620         
12621         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12622             return;
12623         }
12624         
12625         Roo.each(this.selectorEl.dom.files, function(file){    
12626             this.addFile(file);
12627         }, this);
12628          
12629     },
12630     
12631       
12632     
12633       
12634     
12635     addFile : function(file)
12636     {
12637            
12638         if(typeof(file) === 'string'){
12639             throw "Add file by name?"; // should not happen
12640             return;
12641         }
12642         
12643         if(!file || !this.urlAPI){
12644             return;
12645         }
12646         
12647         // file;
12648         // file.type;
12649         
12650         var _this = this;
12651         
12652         
12653         var url = _this.urlAPI.createObjectURL( file);
12654            
12655         this.addCard({
12656             id : Roo.bootstrap.CardUploader.ID--,
12657             is_uploaded : false,
12658             src : url,
12659             title : file.name,
12660             mimetype : file.type,
12661             preview : false,
12662             is_deleted : 0
12663         })
12664         
12665     },
12666     
12667     addCard : function (data)
12668     {
12669         // hidden input element?
12670         // if the file is not an image...
12671         //then we need to use something other that and header_image
12672         var t = this;
12673         //   remove.....
12674         var footer = [
12675             {
12676                 xns : Roo.bootstrap,
12677                 xtype : 'CardFooter',
12678                 items: [
12679                     {
12680                         xns : Roo.bootstrap,
12681                         xtype : 'Element',
12682                         cls : 'd-flex',
12683                         items : [
12684                             
12685                             {
12686                                 xns : Roo.bootstrap,
12687                                 xtype : 'Button',
12688                                 html : String.format("<small>{0}</small>", data.title),
12689                                 cls : 'col-11 text-left',
12690                                 size: 'sm',
12691                                 weight: 'link',
12692                                 fa : 'download',
12693                                 listeners : {
12694                                     click : function() {
12695                                         this.downloadCard(data.id)
12696                                     }
12697                                 }
12698                             },
12699                           
12700                             {
12701                                 xns : Roo.bootstrap,
12702                                 xtype : 'Button',
12703                                 
12704                                 size : 'sm',
12705                                 weight: 'danger',
12706                                 cls : 'col-1',
12707                                 fa : 'times',
12708                                 listeners : {
12709                                     click : function() {
12710                                         t.removeCard(data.id)
12711                                     }
12712                                 }
12713                             }
12714                         ]
12715                     }
12716                     
12717                 ] 
12718             }
12719             
12720         ];
12721
12722         var cn = this.addxtype(
12723             {
12724                  
12725                 xns : Roo.bootstrap,
12726                 xtype : 'Card',
12727                 closeable : true,
12728                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12729                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12730                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12731                 data : data,
12732                 html : false,
12733                  
12734                 items : footer,
12735                 initEvents : function() {
12736                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12737                     this.imgEl = this.el.select('.card-img-top').first();
12738                     if (this.imgEl) {
12739                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12740                         this.imgEl.set({ 'pointer' : 'cursor' });
12741                                   
12742                     }
12743                     
12744                   
12745                 }
12746                 
12747             }
12748         );
12749         // dont' really need ot update items.
12750         // this.items.push(cn);
12751         this.fileCollection.add(cn);
12752         this.updateInput();
12753         
12754     },
12755     removeCard : function(id)
12756     {
12757         
12758         var card  = this.fileCollection.get(id);
12759         card.data.is_deleted = 1;
12760         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12761         this.fileCollection.remove(card);
12762         //this.items = this.items.filter(function(e) { return e != card });
12763         // dont' really need ot update items.
12764         card.el.dom.parentNode.removeChild(card.el.dom);
12765         
12766     },
12767     reset: function()
12768     {
12769         this.fileCollection.each(function(card) {
12770             card.el.dom.parentNode.removeChild(card.el.dom);    
12771         });
12772         this.fileCollection.clear();
12773         this.updateInput();
12774     },
12775     
12776     updateInput : function()
12777     {
12778         var data = [];
12779         this.fileCollection.each(function(e) {
12780             data.push(e.data);
12781         });
12782         
12783         this.inputEl().dom.value = JSON.stringify(data);
12784     }
12785     
12786     
12787 });
12788
12789
12790 Roo.bootstrap.CardUploader.ID = -1;/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800
12801
12802 /**
12803  * @class Roo.data.SortTypes
12804  * @singleton
12805  * Defines the default sorting (casting?) comparison functions used when sorting data.
12806  */
12807 Roo.data.SortTypes = {
12808     /**
12809      * Default sort that does nothing
12810      * @param {Mixed} s The value being converted
12811      * @return {Mixed} The comparison value
12812      */
12813     none : function(s){
12814         return s;
12815     },
12816     
12817     /**
12818      * The regular expression used to strip tags
12819      * @type {RegExp}
12820      * @property
12821      */
12822     stripTagsRE : /<\/?[^>]+>/gi,
12823     
12824     /**
12825      * Strips all HTML tags to sort on text only
12826      * @param {Mixed} s The value being converted
12827      * @return {String} The comparison value
12828      */
12829     asText : function(s){
12830         return String(s).replace(this.stripTagsRE, "");
12831     },
12832     
12833     /**
12834      * Strips all HTML tags to sort on text only - Case insensitive
12835      * @param {Mixed} s The value being converted
12836      * @return {String} The comparison value
12837      */
12838     asUCText : function(s){
12839         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12840     },
12841     
12842     /**
12843      * Case insensitive string
12844      * @param {Mixed} s The value being converted
12845      * @return {String} The comparison value
12846      */
12847     asUCString : function(s) {
12848         return String(s).toUpperCase();
12849     },
12850     
12851     /**
12852      * Date sorting
12853      * @param {Mixed} s The value being converted
12854      * @return {Number} The comparison value
12855      */
12856     asDate : function(s) {
12857         if(!s){
12858             return 0;
12859         }
12860         if(s instanceof Date){
12861             return s.getTime();
12862         }
12863         return Date.parse(String(s));
12864     },
12865     
12866     /**
12867      * Float sorting
12868      * @param {Mixed} s The value being converted
12869      * @return {Float} The comparison value
12870      */
12871     asFloat : function(s) {
12872         var val = parseFloat(String(s).replace(/,/g, ""));
12873         if(isNaN(val)) {
12874             val = 0;
12875         }
12876         return val;
12877     },
12878     
12879     /**
12880      * Integer sorting
12881      * @param {Mixed} s The value being converted
12882      * @return {Number} The comparison value
12883      */
12884     asInt : function(s) {
12885         var val = parseInt(String(s).replace(/,/g, ""));
12886         if(isNaN(val)) {
12887             val = 0;
12888         }
12889         return val;
12890     }
12891 };/*
12892  * Based on:
12893  * Ext JS Library 1.1.1
12894  * Copyright(c) 2006-2007, Ext JS, LLC.
12895  *
12896  * Originally Released Under LGPL - original licence link has changed is not relivant.
12897  *
12898  * Fork - LGPL
12899  * <script type="text/javascript">
12900  */
12901
12902 /**
12903 * @class Roo.data.Record
12904  * Instances of this class encapsulate both record <em>definition</em> information, and record
12905  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12906  * to access Records cached in an {@link Roo.data.Store} object.<br>
12907  * <p>
12908  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12909  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12910  * objects.<br>
12911  * <p>
12912  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12913  * @constructor
12914  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12915  * {@link #create}. The parameters are the same.
12916  * @param {Array} data An associative Array of data values keyed by the field name.
12917  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12918  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12919  * not specified an integer id is generated.
12920  */
12921 Roo.data.Record = function(data, id){
12922     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12923     this.data = data;
12924 };
12925
12926 /**
12927  * Generate a constructor for a specific record layout.
12928  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12929  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12930  * Each field definition object may contain the following properties: <ul>
12931  * <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,
12932  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12933  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12934  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12935  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12936  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12937  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12938  * this may be omitted.</p></li>
12939  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12940  * <ul><li>auto (Default, implies no conversion)</li>
12941  * <li>string</li>
12942  * <li>int</li>
12943  * <li>float</li>
12944  * <li>boolean</li>
12945  * <li>date</li></ul></p></li>
12946  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12947  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12948  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12949  * by the Reader into an object that will be stored in the Record. It is passed the
12950  * following parameters:<ul>
12951  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12952  * </ul></p></li>
12953  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12954  * </ul>
12955  * <br>usage:<br><pre><code>
12956 var TopicRecord = Roo.data.Record.create(
12957     {name: 'title', mapping: 'topic_title'},
12958     {name: 'author', mapping: 'username'},
12959     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12960     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12961     {name: 'lastPoster', mapping: 'user2'},
12962     {name: 'excerpt', mapping: 'post_text'}
12963 );
12964
12965 var myNewRecord = new TopicRecord({
12966     title: 'Do my job please',
12967     author: 'noobie',
12968     totalPosts: 1,
12969     lastPost: new Date(),
12970     lastPoster: 'Animal',
12971     excerpt: 'No way dude!'
12972 });
12973 myStore.add(myNewRecord);
12974 </code></pre>
12975  * @method create
12976  * @static
12977  */
12978 Roo.data.Record.create = function(o){
12979     var f = function(){
12980         f.superclass.constructor.apply(this, arguments);
12981     };
12982     Roo.extend(f, Roo.data.Record);
12983     var p = f.prototype;
12984     p.fields = new Roo.util.MixedCollection(false, function(field){
12985         return field.name;
12986     });
12987     for(var i = 0, len = o.length; i < len; i++){
12988         p.fields.add(new Roo.data.Field(o[i]));
12989     }
12990     f.getField = function(name){
12991         return p.fields.get(name);  
12992     };
12993     return f;
12994 };
12995
12996 Roo.data.Record.AUTO_ID = 1000;
12997 Roo.data.Record.EDIT = 'edit';
12998 Roo.data.Record.REJECT = 'reject';
12999 Roo.data.Record.COMMIT = 'commit';
13000
13001 Roo.data.Record.prototype = {
13002     /**
13003      * Readonly flag - true if this record has been modified.
13004      * @type Boolean
13005      */
13006     dirty : false,
13007     editing : false,
13008     error: null,
13009     modified: null,
13010
13011     // private
13012     join : function(store){
13013         this.store = store;
13014     },
13015
13016     /**
13017      * Set the named field to the specified value.
13018      * @param {String} name The name of the field to set.
13019      * @param {Object} value The value to set the field to.
13020      */
13021     set : function(name, value){
13022         if(this.data[name] == value){
13023             return;
13024         }
13025         this.dirty = true;
13026         if(!this.modified){
13027             this.modified = {};
13028         }
13029         if(typeof this.modified[name] == 'undefined'){
13030             this.modified[name] = this.data[name];
13031         }
13032         this.data[name] = value;
13033         if(!this.editing && this.store){
13034             this.store.afterEdit(this);
13035         }       
13036     },
13037
13038     /**
13039      * Get the value of the named field.
13040      * @param {String} name The name of the field to get the value of.
13041      * @return {Object} The value of the field.
13042      */
13043     get : function(name){
13044         return this.data[name]; 
13045     },
13046
13047     // private
13048     beginEdit : function(){
13049         this.editing = true;
13050         this.modified = {}; 
13051     },
13052
13053     // private
13054     cancelEdit : function(){
13055         this.editing = false;
13056         delete this.modified;
13057     },
13058
13059     // private
13060     endEdit : function(){
13061         this.editing = false;
13062         if(this.dirty && this.store){
13063             this.store.afterEdit(this);
13064         }
13065     },
13066
13067     /**
13068      * Usually called by the {@link Roo.data.Store} which owns the Record.
13069      * Rejects all changes made to the Record since either creation, or the last commit operation.
13070      * Modified fields are reverted to their original values.
13071      * <p>
13072      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13073      * of reject operations.
13074      */
13075     reject : function(){
13076         var m = this.modified;
13077         for(var n in m){
13078             if(typeof m[n] != "function"){
13079                 this.data[n] = m[n];
13080             }
13081         }
13082         this.dirty = false;
13083         delete this.modified;
13084         this.editing = false;
13085         if(this.store){
13086             this.store.afterReject(this);
13087         }
13088     },
13089
13090     /**
13091      * Usually called by the {@link Roo.data.Store} which owns the Record.
13092      * Commits all changes made to the Record since either creation, or the last commit operation.
13093      * <p>
13094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095      * of commit operations.
13096      */
13097     commit : function(){
13098         this.dirty = false;
13099         delete this.modified;
13100         this.editing = false;
13101         if(this.store){
13102             this.store.afterCommit(this);
13103         }
13104     },
13105
13106     // private
13107     hasError : function(){
13108         return this.error != null;
13109     },
13110
13111     // private
13112     clearError : function(){
13113         this.error = null;
13114     },
13115
13116     /**
13117      * Creates a copy of this record.
13118      * @param {String} id (optional) A new record id if you don't want to use this record's id
13119      * @return {Record}
13120      */
13121     copy : function(newId) {
13122         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13123     }
13124 };/*
13125  * Based on:
13126  * Ext JS Library 1.1.1
13127  * Copyright(c) 2006-2007, Ext JS, LLC.
13128  *
13129  * Originally Released Under LGPL - original licence link has changed is not relivant.
13130  *
13131  * Fork - LGPL
13132  * <script type="text/javascript">
13133  */
13134
13135
13136
13137 /**
13138  * @class Roo.data.Store
13139  * @extends Roo.util.Observable
13140  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13141  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13142  * <p>
13143  * 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
13144  * has no knowledge of the format of the data returned by the Proxy.<br>
13145  * <p>
13146  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13147  * instances from the data object. These records are cached and made available through accessor functions.
13148  * @constructor
13149  * Creates a new Store.
13150  * @param {Object} config A config object containing the objects needed for the Store to access data,
13151  * and read the data into Records.
13152  */
13153 Roo.data.Store = function(config){
13154     this.data = new Roo.util.MixedCollection(false);
13155     this.data.getKey = function(o){
13156         return o.id;
13157     };
13158     this.baseParams = {};
13159     // private
13160     this.paramNames = {
13161         "start" : "start",
13162         "limit" : "limit",
13163         "sort" : "sort",
13164         "dir" : "dir",
13165         "multisort" : "_multisort"
13166     };
13167
13168     if(config && config.data){
13169         this.inlineData = config.data;
13170         delete config.data;
13171     }
13172
13173     Roo.apply(this, config);
13174     
13175     if(this.reader){ // reader passed
13176         this.reader = Roo.factory(this.reader, Roo.data);
13177         this.reader.xmodule = this.xmodule || false;
13178         if(!this.recordType){
13179             this.recordType = this.reader.recordType;
13180         }
13181         if(this.reader.onMetaChange){
13182             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13183         }
13184     }
13185
13186     if(this.recordType){
13187         this.fields = this.recordType.prototype.fields;
13188     }
13189     this.modified = [];
13190
13191     this.addEvents({
13192         /**
13193          * @event datachanged
13194          * Fires when the data cache has changed, and a widget which is using this Store
13195          * as a Record cache should refresh its view.
13196          * @param {Store} this
13197          */
13198         datachanged : true,
13199         /**
13200          * @event metachange
13201          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13202          * @param {Store} this
13203          * @param {Object} meta The JSON metadata
13204          */
13205         metachange : true,
13206         /**
13207          * @event add
13208          * Fires when Records have been added to the Store
13209          * @param {Store} this
13210          * @param {Roo.data.Record[]} records The array of Records added
13211          * @param {Number} index The index at which the record(s) were added
13212          */
13213         add : true,
13214         /**
13215          * @event remove
13216          * Fires when a Record has been removed from the Store
13217          * @param {Store} this
13218          * @param {Roo.data.Record} record The Record that was removed
13219          * @param {Number} index The index at which the record was removed
13220          */
13221         remove : true,
13222         /**
13223          * @event update
13224          * Fires when a Record has been updated
13225          * @param {Store} this
13226          * @param {Roo.data.Record} record The Record that was updated
13227          * @param {String} operation The update operation being performed.  Value may be one of:
13228          * <pre><code>
13229  Roo.data.Record.EDIT
13230  Roo.data.Record.REJECT
13231  Roo.data.Record.COMMIT
13232          * </code></pre>
13233          */
13234         update : true,
13235         /**
13236          * @event clear
13237          * Fires when the data cache has been cleared.
13238          * @param {Store} this
13239          */
13240         clear : true,
13241         /**
13242          * @event beforeload
13243          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13244          * the load action will be canceled.
13245          * @param {Store} this
13246          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13247          */
13248         beforeload : true,
13249         /**
13250          * @event beforeloadadd
13251          * Fires after a new set of Records has been loaded.
13252          * @param {Store} this
13253          * @param {Roo.data.Record[]} records The Records that were loaded
13254          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13255          */
13256         beforeloadadd : true,
13257         /**
13258          * @event load
13259          * Fires after a new set of Records has been loaded, before they are added to the store.
13260          * @param {Store} this
13261          * @param {Roo.data.Record[]} records The Records that were loaded
13262          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13263          * @params {Object} return from reader
13264          */
13265         load : true,
13266         /**
13267          * @event loadexception
13268          * Fires if an exception occurs in the Proxy during loading.
13269          * Called with the signature of the Proxy's "loadexception" event.
13270          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13271          * 
13272          * @param {Proxy} 
13273          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13274          * @param {Object} load options 
13275          * @param {Object} jsonData from your request (normally this contains the Exception)
13276          */
13277         loadexception : true
13278     });
13279     
13280     if(this.proxy){
13281         this.proxy = Roo.factory(this.proxy, Roo.data);
13282         this.proxy.xmodule = this.xmodule || false;
13283         this.relayEvents(this.proxy,  ["loadexception"]);
13284     }
13285     this.sortToggle = {};
13286     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13287
13288     Roo.data.Store.superclass.constructor.call(this);
13289
13290     if(this.inlineData){
13291         this.loadData(this.inlineData);
13292         delete this.inlineData;
13293     }
13294 };
13295
13296 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13297      /**
13298     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13299     * without a remote query - used by combo/forms at present.
13300     */
13301     
13302     /**
13303     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13304     */
13305     /**
13306     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13307     */
13308     /**
13309     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13310     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13311     */
13312     /**
13313     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13314     * on any HTTP request
13315     */
13316     /**
13317     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13318     */
13319     /**
13320     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13321     */
13322     multiSort: false,
13323     /**
13324     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13325     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13326     */
13327     remoteSort : false,
13328
13329     /**
13330     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13331      * loaded or when a record is removed. (defaults to false).
13332     */
13333     pruneModifiedRecords : false,
13334
13335     // private
13336     lastOptions : null,
13337
13338     /**
13339      * Add Records to the Store and fires the add event.
13340      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13341      */
13342     add : function(records){
13343         records = [].concat(records);
13344         for(var i = 0, len = records.length; i < len; i++){
13345             records[i].join(this);
13346         }
13347         var index = this.data.length;
13348         this.data.addAll(records);
13349         this.fireEvent("add", this, records, index);
13350     },
13351
13352     /**
13353      * Remove a Record from the Store and fires the remove event.
13354      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13355      */
13356     remove : function(record){
13357         var index = this.data.indexOf(record);
13358         this.data.removeAt(index);
13359  
13360         if(this.pruneModifiedRecords){
13361             this.modified.remove(record);
13362         }
13363         this.fireEvent("remove", this, record, index);
13364     },
13365
13366     /**
13367      * Remove all Records from the Store and fires the clear event.
13368      */
13369     removeAll : function(){
13370         this.data.clear();
13371         if(this.pruneModifiedRecords){
13372             this.modified = [];
13373         }
13374         this.fireEvent("clear", this);
13375     },
13376
13377     /**
13378      * Inserts Records to the Store at the given index and fires the add event.
13379      * @param {Number} index The start index at which to insert the passed Records.
13380      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13381      */
13382     insert : function(index, records){
13383         records = [].concat(records);
13384         for(var i = 0, len = records.length; i < len; i++){
13385             this.data.insert(index, records[i]);
13386             records[i].join(this);
13387         }
13388         this.fireEvent("add", this, records, index);
13389     },
13390
13391     /**
13392      * Get the index within the cache of the passed Record.
13393      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13394      * @return {Number} The index of the passed Record. Returns -1 if not found.
13395      */
13396     indexOf : function(record){
13397         return this.data.indexOf(record);
13398     },
13399
13400     /**
13401      * Get the index within the cache of the Record with the passed id.
13402      * @param {String} id The id of the Record to find.
13403      * @return {Number} The index of the Record. Returns -1 if not found.
13404      */
13405     indexOfId : function(id){
13406         return this.data.indexOfKey(id);
13407     },
13408
13409     /**
13410      * Get the Record with the specified id.
13411      * @param {String} id The id of the Record to find.
13412      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13413      */
13414     getById : function(id){
13415         return this.data.key(id);
13416     },
13417
13418     /**
13419      * Get the Record at the specified index.
13420      * @param {Number} index The index of the Record to find.
13421      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13422      */
13423     getAt : function(index){
13424         return this.data.itemAt(index);
13425     },
13426
13427     /**
13428      * Returns a range of Records between specified indices.
13429      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13430      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13431      * @return {Roo.data.Record[]} An array of Records
13432      */
13433     getRange : function(start, end){
13434         return this.data.getRange(start, end);
13435     },
13436
13437     // private
13438     storeOptions : function(o){
13439         o = Roo.apply({}, o);
13440         delete o.callback;
13441         delete o.scope;
13442         this.lastOptions = o;
13443     },
13444
13445     /**
13446      * Loads the Record cache from the configured Proxy using the configured Reader.
13447      * <p>
13448      * If using remote paging, then the first load call must specify the <em>start</em>
13449      * and <em>limit</em> properties in the options.params property to establish the initial
13450      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13451      * <p>
13452      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13453      * and this call will return before the new data has been loaded. Perform any post-processing
13454      * in a callback function, or in a "load" event handler.</strong>
13455      * <p>
13456      * @param {Object} options An object containing properties which control loading options:<ul>
13457      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13458      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13459      * passed the following arguments:<ul>
13460      * <li>r : Roo.data.Record[]</li>
13461      * <li>options: Options object from the load call</li>
13462      * <li>success: Boolean success indicator</li></ul></li>
13463      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13464      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13465      * </ul>
13466      */
13467     load : function(options){
13468         options = options || {};
13469         if(this.fireEvent("beforeload", this, options) !== false){
13470             this.storeOptions(options);
13471             var p = Roo.apply(options.params || {}, this.baseParams);
13472             // if meta was not loaded from remote source.. try requesting it.
13473             if (!this.reader.metaFromRemote) {
13474                 p._requestMeta = 1;
13475             }
13476             if(this.sortInfo && this.remoteSort){
13477                 var pn = this.paramNames;
13478                 p[pn["sort"]] = this.sortInfo.field;
13479                 p[pn["dir"]] = this.sortInfo.direction;
13480             }
13481             if (this.multiSort) {
13482                 var pn = this.paramNames;
13483                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13484             }
13485             
13486             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13487         }
13488     },
13489
13490     /**
13491      * Reloads the Record cache from the configured Proxy using the configured Reader and
13492      * the options from the last load operation performed.
13493      * @param {Object} options (optional) An object containing properties which may override the options
13494      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13495      * the most recently used options are reused).
13496      */
13497     reload : function(options){
13498         this.load(Roo.applyIf(options||{}, this.lastOptions));
13499     },
13500
13501     // private
13502     // Called as a callback by the Reader during a load operation.
13503     loadRecords : function(o, options, success){
13504         if(!o || success === false){
13505             if(success !== false){
13506                 this.fireEvent("load", this, [], options, o);
13507             }
13508             if(options.callback){
13509                 options.callback.call(options.scope || this, [], options, false);
13510             }
13511             return;
13512         }
13513         // if data returned failure - throw an exception.
13514         if (o.success === false) {
13515             // show a message if no listener is registered.
13516             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13517                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13518             }
13519             // loadmask wil be hooked into this..
13520             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13521             return;
13522         }
13523         var r = o.records, t = o.totalRecords || r.length;
13524         
13525         this.fireEvent("beforeloadadd", this, r, options, o);
13526         
13527         if(!options || options.add !== true){
13528             if(this.pruneModifiedRecords){
13529                 this.modified = [];
13530             }
13531             for(var i = 0, len = r.length; i < len; i++){
13532                 r[i].join(this);
13533             }
13534             if(this.snapshot){
13535                 this.data = this.snapshot;
13536                 delete this.snapshot;
13537             }
13538             this.data.clear();
13539             this.data.addAll(r);
13540             this.totalLength = t;
13541             this.applySort();
13542             this.fireEvent("datachanged", this);
13543         }else{
13544             this.totalLength = Math.max(t, this.data.length+r.length);
13545             this.add(r);
13546         }
13547         
13548         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13549                 
13550             var e = new Roo.data.Record({});
13551
13552             e.set(this.parent.displayField, this.parent.emptyTitle);
13553             e.set(this.parent.valueField, '');
13554
13555             this.insert(0, e);
13556         }
13557             
13558         this.fireEvent("load", this, r, options, o);
13559         if(options.callback){
13560             options.callback.call(options.scope || this, r, options, true);
13561         }
13562     },
13563
13564
13565     /**
13566      * Loads data from a passed data block. A Reader which understands the format of the data
13567      * must have been configured in the constructor.
13568      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13569      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13570      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13571      */
13572     loadData : function(o, append){
13573         var r = this.reader.readRecords(o);
13574         this.loadRecords(r, {add: append}, true);
13575     },
13576     
13577      /**
13578      * using 'cn' the nested child reader read the child array into it's child stores.
13579      * @param {Object} rec The record with a 'children array
13580      */
13581     loadDataFromChildren : function(rec)
13582     {
13583         this.loadData(this.reader.toLoadData(rec));
13584     },
13585     
13586
13587     /**
13588      * Gets the number of cached records.
13589      * <p>
13590      * <em>If using paging, this may not be the total size of the dataset. If the data object
13591      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13592      * the data set size</em>
13593      */
13594     getCount : function(){
13595         return this.data.length || 0;
13596     },
13597
13598     /**
13599      * Gets the total number of records in the dataset as returned by the server.
13600      * <p>
13601      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13602      * the dataset size</em>
13603      */
13604     getTotalCount : function(){
13605         return this.totalLength || 0;
13606     },
13607
13608     /**
13609      * Returns the sort state of the Store as an object with two properties:
13610      * <pre><code>
13611  field {String} The name of the field by which the Records are sorted
13612  direction {String} The sort order, "ASC" or "DESC"
13613      * </code></pre>
13614      */
13615     getSortState : function(){
13616         return this.sortInfo;
13617     },
13618
13619     // private
13620     applySort : function(){
13621         if(this.sortInfo && !this.remoteSort){
13622             var s = this.sortInfo, f = s.field;
13623             var st = this.fields.get(f).sortType;
13624             var fn = function(r1, r2){
13625                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13626                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13627             };
13628             this.data.sort(s.direction, fn);
13629             if(this.snapshot && this.snapshot != this.data){
13630                 this.snapshot.sort(s.direction, fn);
13631             }
13632         }
13633     },
13634
13635     /**
13636      * Sets the default sort column and order to be used by the next load operation.
13637      * @param {String} fieldName The name of the field to sort by.
13638      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13639      */
13640     setDefaultSort : function(field, dir){
13641         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13642     },
13643
13644     /**
13645      * Sort the Records.
13646      * If remote sorting is used, the sort is performed on the server, and the cache is
13647      * reloaded. If local sorting is used, the cache is sorted internally.
13648      * @param {String} fieldName The name of the field to sort by.
13649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13650      */
13651     sort : function(fieldName, dir){
13652         var f = this.fields.get(fieldName);
13653         if(!dir){
13654             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13655             
13656             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13657                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13658             }else{
13659                 dir = f.sortDir;
13660             }
13661         }
13662         this.sortToggle[f.name] = dir;
13663         this.sortInfo = {field: f.name, direction: dir};
13664         if(!this.remoteSort){
13665             this.applySort();
13666             this.fireEvent("datachanged", this);
13667         }else{
13668             this.load(this.lastOptions);
13669         }
13670     },
13671
13672     /**
13673      * Calls the specified function for each of the Records in the cache.
13674      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13675      * Returning <em>false</em> aborts and exits the iteration.
13676      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13677      */
13678     each : function(fn, scope){
13679         this.data.each(fn, scope);
13680     },
13681
13682     /**
13683      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13684      * (e.g., during paging).
13685      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13686      */
13687     getModifiedRecords : function(){
13688         return this.modified;
13689     },
13690
13691     // private
13692     createFilterFn : function(property, value, anyMatch){
13693         if(!value.exec){ // not a regex
13694             value = String(value);
13695             if(value.length == 0){
13696                 return false;
13697             }
13698             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13699         }
13700         return function(r){
13701             return value.test(r.data[property]);
13702         };
13703     },
13704
13705     /**
13706      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13707      * @param {String} property A field on your records
13708      * @param {Number} start The record index to start at (defaults to 0)
13709      * @param {Number} end The last record index to include (defaults to length - 1)
13710      * @return {Number} The sum
13711      */
13712     sum : function(property, start, end){
13713         var rs = this.data.items, v = 0;
13714         start = start || 0;
13715         end = (end || end === 0) ? end : rs.length-1;
13716
13717         for(var i = start; i <= end; i++){
13718             v += (rs[i].data[property] || 0);
13719         }
13720         return v;
13721     },
13722
13723     /**
13724      * Filter the records by a specified property.
13725      * @param {String} field A field on your records
13726      * @param {String/RegExp} value Either a string that the field
13727      * should start with or a RegExp to test against the field
13728      * @param {Boolean} anyMatch True to match any part not just the beginning
13729      */
13730     filter : function(property, value, anyMatch){
13731         var fn = this.createFilterFn(property, value, anyMatch);
13732         return fn ? this.filterBy(fn) : this.clearFilter();
13733     },
13734
13735     /**
13736      * Filter by a function. The specified function will be called with each
13737      * record in this data source. If the function returns true the record is included,
13738      * otherwise it is filtered.
13739      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13740      * @param {Object} scope (optional) The scope of the function (defaults to this)
13741      */
13742     filterBy : function(fn, scope){
13743         this.snapshot = this.snapshot || this.data;
13744         this.data = this.queryBy(fn, scope||this);
13745         this.fireEvent("datachanged", this);
13746     },
13747
13748     /**
13749      * Query the records by a specified property.
13750      * @param {String} field A field on your records
13751      * @param {String/RegExp} value Either a string that the field
13752      * should start with or a RegExp to test against the field
13753      * @param {Boolean} anyMatch True to match any part not just the beginning
13754      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13755      */
13756     query : function(property, value, anyMatch){
13757         var fn = this.createFilterFn(property, value, anyMatch);
13758         return fn ? this.queryBy(fn) : this.data.clone();
13759     },
13760
13761     /**
13762      * Query by a function. The specified function will be called with each
13763      * record in this data source. If the function returns true the record is included
13764      * in the results.
13765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766      * @param {Object} scope (optional) The scope of the function (defaults to this)
13767       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13768      **/
13769     queryBy : function(fn, scope){
13770         var data = this.snapshot || this.data;
13771         return data.filterBy(fn, scope||this);
13772     },
13773
13774     /**
13775      * Collects unique values for a particular dataIndex from this store.
13776      * @param {String} dataIndex The property to collect
13777      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13778      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13779      * @return {Array} An array of the unique values
13780      **/
13781     collect : function(dataIndex, allowNull, bypassFilter){
13782         var d = (bypassFilter === true && this.snapshot) ?
13783                 this.snapshot.items : this.data.items;
13784         var v, sv, r = [], l = {};
13785         for(var i = 0, len = d.length; i < len; i++){
13786             v = d[i].data[dataIndex];
13787             sv = String(v);
13788             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13789                 l[sv] = true;
13790                 r[r.length] = v;
13791             }
13792         }
13793         return r;
13794     },
13795
13796     /**
13797      * Revert to a view of the Record cache with no filtering applied.
13798      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13799      */
13800     clearFilter : function(suppressEvent){
13801         if(this.snapshot && this.snapshot != this.data){
13802             this.data = this.snapshot;
13803             delete this.snapshot;
13804             if(suppressEvent !== true){
13805                 this.fireEvent("datachanged", this);
13806             }
13807         }
13808     },
13809
13810     // private
13811     afterEdit : function(record){
13812         if(this.modified.indexOf(record) == -1){
13813             this.modified.push(record);
13814         }
13815         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13816     },
13817     
13818     // private
13819     afterReject : function(record){
13820         this.modified.remove(record);
13821         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13822     },
13823
13824     // private
13825     afterCommit : function(record){
13826         this.modified.remove(record);
13827         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13828     },
13829
13830     /**
13831      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13832      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13833      */
13834     commitChanges : function(){
13835         var m = this.modified.slice(0);
13836         this.modified = [];
13837         for(var i = 0, len = m.length; i < len; i++){
13838             m[i].commit();
13839         }
13840     },
13841
13842     /**
13843      * Cancel outstanding changes on all changed records.
13844      */
13845     rejectChanges : function(){
13846         var m = this.modified.slice(0);
13847         this.modified = [];
13848         for(var i = 0, len = m.length; i < len; i++){
13849             m[i].reject();
13850         }
13851     },
13852
13853     onMetaChange : function(meta, rtype, o){
13854         this.recordType = rtype;
13855         this.fields = rtype.prototype.fields;
13856         delete this.snapshot;
13857         this.sortInfo = meta.sortInfo || this.sortInfo;
13858         this.modified = [];
13859         this.fireEvent('metachange', this, this.reader.meta);
13860     },
13861     
13862     moveIndex : function(data, type)
13863     {
13864         var index = this.indexOf(data);
13865         
13866         var newIndex = index + type;
13867         
13868         this.remove(data);
13869         
13870         this.insert(newIndex, data);
13871         
13872     }
13873 });/*
13874  * Based on:
13875  * Ext JS Library 1.1.1
13876  * Copyright(c) 2006-2007, Ext JS, LLC.
13877  *
13878  * Originally Released Under LGPL - original licence link has changed is not relivant.
13879  *
13880  * Fork - LGPL
13881  * <script type="text/javascript">
13882  */
13883
13884 /**
13885  * @class Roo.data.SimpleStore
13886  * @extends Roo.data.Store
13887  * Small helper class to make creating Stores from Array data easier.
13888  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13889  * @cfg {Array} fields An array of field definition objects, or field name strings.
13890  * @cfg {Object} an existing reader (eg. copied from another store)
13891  * @cfg {Array} data The multi-dimensional array of data
13892  * @constructor
13893  * @param {Object} config
13894  */
13895 Roo.data.SimpleStore = function(config)
13896 {
13897     Roo.data.SimpleStore.superclass.constructor.call(this, {
13898         isLocal : true,
13899         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13900                 id: config.id
13901             },
13902             Roo.data.Record.create(config.fields)
13903         ),
13904         proxy : new Roo.data.MemoryProxy(config.data)
13905     });
13906     this.load();
13907 };
13908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13909  * Based on:
13910  * Ext JS Library 1.1.1
13911  * Copyright(c) 2006-2007, Ext JS, LLC.
13912  *
13913  * Originally Released Under LGPL - original licence link has changed is not relivant.
13914  *
13915  * Fork - LGPL
13916  * <script type="text/javascript">
13917  */
13918
13919 /**
13920 /**
13921  * @extends Roo.data.Store
13922  * @class Roo.data.JsonStore
13923  * Small helper class to make creating Stores for JSON data easier. <br/>
13924 <pre><code>
13925 var store = new Roo.data.JsonStore({
13926     url: 'get-images.php',
13927     root: 'images',
13928     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13929 });
13930 </code></pre>
13931  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13932  * JsonReader and HttpProxy (unless inline data is provided).</b>
13933  * @cfg {Array} fields An array of field definition objects, or field name strings.
13934  * @constructor
13935  * @param {Object} config
13936  */
13937 Roo.data.JsonStore = function(c){
13938     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13939         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13940         reader: new Roo.data.JsonReader(c, c.fields)
13941     }));
13942 };
13943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13944  * Based on:
13945  * Ext JS Library 1.1.1
13946  * Copyright(c) 2006-2007, Ext JS, LLC.
13947  *
13948  * Originally Released Under LGPL - original licence link has changed is not relivant.
13949  *
13950  * Fork - LGPL
13951  * <script type="text/javascript">
13952  */
13953
13954  
13955 Roo.data.Field = function(config){
13956     if(typeof config == "string"){
13957         config = {name: config};
13958     }
13959     Roo.apply(this, config);
13960     
13961     if(!this.type){
13962         this.type = "auto";
13963     }
13964     
13965     var st = Roo.data.SortTypes;
13966     // named sortTypes are supported, here we look them up
13967     if(typeof this.sortType == "string"){
13968         this.sortType = st[this.sortType];
13969     }
13970     
13971     // set default sortType for strings and dates
13972     if(!this.sortType){
13973         switch(this.type){
13974             case "string":
13975                 this.sortType = st.asUCString;
13976                 break;
13977             case "date":
13978                 this.sortType = st.asDate;
13979                 break;
13980             default:
13981                 this.sortType = st.none;
13982         }
13983     }
13984
13985     // define once
13986     var stripRe = /[\$,%]/g;
13987
13988     // prebuilt conversion function for this field, instead of
13989     // switching every time we're reading a value
13990     if(!this.convert){
13991         var cv, dateFormat = this.dateFormat;
13992         switch(this.type){
13993             case "":
13994             case "auto":
13995             case undefined:
13996                 cv = function(v){ return v; };
13997                 break;
13998             case "string":
13999                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14000                 break;
14001             case "int":
14002                 cv = function(v){
14003                     return v !== undefined && v !== null && v !== '' ?
14004                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14005                     };
14006                 break;
14007             case "float":
14008                 cv = function(v){
14009                     return v !== undefined && v !== null && v !== '' ?
14010                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14011                     };
14012                 break;
14013             case "bool":
14014             case "boolean":
14015                 cv = function(v){ return v === true || v === "true" || v == 1; };
14016                 break;
14017             case "date":
14018                 cv = function(v){
14019                     if(!v){
14020                         return '';
14021                     }
14022                     if(v instanceof Date){
14023                         return v;
14024                     }
14025                     if(dateFormat){
14026                         if(dateFormat == "timestamp"){
14027                             return new Date(v*1000);
14028                         }
14029                         return Date.parseDate(v, dateFormat);
14030                     }
14031                     var parsed = Date.parse(v);
14032                     return parsed ? new Date(parsed) : null;
14033                 };
14034              break;
14035             
14036         }
14037         this.convert = cv;
14038     }
14039 };
14040
14041 Roo.data.Field.prototype = {
14042     dateFormat: null,
14043     defaultValue: "",
14044     mapping: null,
14045     sortType : null,
14046     sortDir : "ASC"
14047 };/*
14048  * Based on:
14049  * Ext JS Library 1.1.1
14050  * Copyright(c) 2006-2007, Ext JS, LLC.
14051  *
14052  * Originally Released Under LGPL - original licence link has changed is not relivant.
14053  *
14054  * Fork - LGPL
14055  * <script type="text/javascript">
14056  */
14057  
14058 // Base class for reading structured data from a data source.  This class is intended to be
14059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14060
14061 /**
14062  * @class Roo.data.DataReader
14063  * Base class for reading structured data from a data source.  This class is intended to be
14064  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14065  */
14066
14067 Roo.data.DataReader = function(meta, recordType){
14068     
14069     this.meta = meta;
14070     
14071     this.recordType = recordType instanceof Array ? 
14072         Roo.data.Record.create(recordType) : recordType;
14073 };
14074
14075 Roo.data.DataReader.prototype = {
14076     
14077     
14078     readerType : 'Data',
14079      /**
14080      * Create an empty record
14081      * @param {Object} data (optional) - overlay some values
14082      * @return {Roo.data.Record} record created.
14083      */
14084     newRow :  function(d) {
14085         var da =  {};
14086         this.recordType.prototype.fields.each(function(c) {
14087             switch( c.type) {
14088                 case 'int' : da[c.name] = 0; break;
14089                 case 'date' : da[c.name] = new Date(); break;
14090                 case 'float' : da[c.name] = 0.0; break;
14091                 case 'boolean' : da[c.name] = false; break;
14092                 default : da[c.name] = ""; break;
14093             }
14094             
14095         });
14096         return new this.recordType(Roo.apply(da, d));
14097     }
14098     
14099     
14100 };/*
14101  * Based on:
14102  * Ext JS Library 1.1.1
14103  * Copyright(c) 2006-2007, Ext JS, LLC.
14104  *
14105  * Originally Released Under LGPL - original licence link has changed is not relivant.
14106  *
14107  * Fork - LGPL
14108  * <script type="text/javascript">
14109  */
14110
14111 /**
14112  * @class Roo.data.DataProxy
14113  * @extends Roo.data.Observable
14114  * This class is an abstract base class for implementations which provide retrieval of
14115  * unformatted data objects.<br>
14116  * <p>
14117  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14118  * (of the appropriate type which knows how to parse the data object) to provide a block of
14119  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14120  * <p>
14121  * Custom implementations must implement the load method as described in
14122  * {@link Roo.data.HttpProxy#load}.
14123  */
14124 Roo.data.DataProxy = function(){
14125     this.addEvents({
14126         /**
14127          * @event beforeload
14128          * Fires before a network request is made to retrieve a data object.
14129          * @param {Object} This DataProxy object.
14130          * @param {Object} params The params parameter to the load function.
14131          */
14132         beforeload : true,
14133         /**
14134          * @event load
14135          * Fires before the load method's callback is called.
14136          * @param {Object} This DataProxy object.
14137          * @param {Object} o The data object.
14138          * @param {Object} arg The callback argument object passed to the load function.
14139          */
14140         load : true,
14141         /**
14142          * @event loadexception
14143          * Fires if an Exception occurs during data retrieval.
14144          * @param {Object} This DataProxy object.
14145          * @param {Object} o The data object.
14146          * @param {Object} arg The callback argument object passed to the load function.
14147          * @param {Object} e The Exception.
14148          */
14149         loadexception : true
14150     });
14151     Roo.data.DataProxy.superclass.constructor.call(this);
14152 };
14153
14154 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14155
14156     /**
14157      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14158      */
14159 /*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169 /**
14170  * @class Roo.data.MemoryProxy
14171  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14172  * to the Reader when its load method is called.
14173  * @constructor
14174  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14175  */
14176 Roo.data.MemoryProxy = function(data){
14177     if (data.data) {
14178         data = data.data;
14179     }
14180     Roo.data.MemoryProxy.superclass.constructor.call(this);
14181     this.data = data;
14182 };
14183
14184 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14185     
14186     /**
14187      * Load data from the requested source (in this case an in-memory
14188      * data object passed to the constructor), read the data object into
14189      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14190      * process that block using the passed callback.
14191      * @param {Object} params This parameter is not used by the MemoryProxy class.
14192      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14193      * object into a block of Roo.data.Records.
14194      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14195      * The function must be passed <ul>
14196      * <li>The Record block object</li>
14197      * <li>The "arg" argument from the load function</li>
14198      * <li>A boolean success indicator</li>
14199      * </ul>
14200      * @param {Object} scope The scope in which to call the callback
14201      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14202      */
14203     load : function(params, reader, callback, scope, arg){
14204         params = params || {};
14205         var result;
14206         try {
14207             result = reader.readRecords(params.data ? params.data :this.data);
14208         }catch(e){
14209             this.fireEvent("loadexception", this, arg, null, e);
14210             callback.call(scope, null, arg, false);
14211             return;
14212         }
14213         callback.call(scope, result, arg, true);
14214     },
14215     
14216     // private
14217     update : function(params, records){
14218         
14219     }
14220 });/*
14221  * Based on:
14222  * Ext JS Library 1.1.1
14223  * Copyright(c) 2006-2007, Ext JS, LLC.
14224  *
14225  * Originally Released Under LGPL - original licence link has changed is not relivant.
14226  *
14227  * Fork - LGPL
14228  * <script type="text/javascript">
14229  */
14230 /**
14231  * @class Roo.data.HttpProxy
14232  * @extends Roo.data.DataProxy
14233  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14234  * configured to reference a certain URL.<br><br>
14235  * <p>
14236  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14237  * from which the running page was served.<br><br>
14238  * <p>
14239  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14240  * <p>
14241  * Be aware that to enable the browser to parse an XML document, the server must set
14242  * the Content-Type header in the HTTP response to "text/xml".
14243  * @constructor
14244  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14245  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14246  * will be used to make the request.
14247  */
14248 Roo.data.HttpProxy = function(conn){
14249     Roo.data.HttpProxy.superclass.constructor.call(this);
14250     // is conn a conn config or a real conn?
14251     this.conn = conn;
14252     this.useAjax = !conn || !conn.events;
14253   
14254 };
14255
14256 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14257     // thse are take from connection...
14258     
14259     /**
14260      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14261      */
14262     /**
14263      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14264      * extra parameters to each request made by this object. (defaults to undefined)
14265      */
14266     /**
14267      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14268      *  to each request made by this object. (defaults to undefined)
14269      */
14270     /**
14271      * @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)
14272      */
14273     /**
14274      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14275      */
14276      /**
14277      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14278      * @type Boolean
14279      */
14280   
14281
14282     /**
14283      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14284      * @type Boolean
14285      */
14286     /**
14287      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14288      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14289      * a finer-grained basis than the DataProxy events.
14290      */
14291     getConnection : function(){
14292         return this.useAjax ? Roo.Ajax : this.conn;
14293     },
14294
14295     /**
14296      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14297      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14298      * process that block using the passed callback.
14299      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14300      * for the request to the remote server.
14301      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14302      * object into a block of Roo.data.Records.
14303      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14304      * The function must be passed <ul>
14305      * <li>The Record block object</li>
14306      * <li>The "arg" argument from the load function</li>
14307      * <li>A boolean success indicator</li>
14308      * </ul>
14309      * @param {Object} scope The scope in which to call the callback
14310      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14311      */
14312     load : function(params, reader, callback, scope, arg){
14313         if(this.fireEvent("beforeload", this, params) !== false){
14314             var  o = {
14315                 params : params || {},
14316                 request: {
14317                     callback : callback,
14318                     scope : scope,
14319                     arg : arg
14320                 },
14321                 reader: reader,
14322                 callback : this.loadResponse,
14323                 scope: this
14324             };
14325             if(this.useAjax){
14326                 Roo.applyIf(o, this.conn);
14327                 if(this.activeRequest){
14328                     Roo.Ajax.abort(this.activeRequest);
14329                 }
14330                 this.activeRequest = Roo.Ajax.request(o);
14331             }else{
14332                 this.conn.request(o);
14333             }
14334         }else{
14335             callback.call(scope||this, null, arg, false);
14336         }
14337     },
14338
14339     // private
14340     loadResponse : function(o, success, response){
14341         delete this.activeRequest;
14342         if(!success){
14343             this.fireEvent("loadexception", this, o, response);
14344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14345             return;
14346         }
14347         var result;
14348         try {
14349             result = o.reader.read(response);
14350         }catch(e){
14351             this.fireEvent("loadexception", this, o, response, e);
14352             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14353             return;
14354         }
14355         
14356         this.fireEvent("load", this, o, o.request.arg);
14357         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14358     },
14359
14360     // private
14361     update : function(dataSet){
14362
14363     },
14364
14365     // private
14366     updateResponse : function(dataSet){
14367
14368     }
14369 });/*
14370  * Based on:
14371  * Ext JS Library 1.1.1
14372  * Copyright(c) 2006-2007, Ext JS, LLC.
14373  *
14374  * Originally Released Under LGPL - original licence link has changed is not relivant.
14375  *
14376  * Fork - LGPL
14377  * <script type="text/javascript">
14378  */
14379
14380 /**
14381  * @class Roo.data.ScriptTagProxy
14382  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14383  * other than the originating domain of the running page.<br><br>
14384  * <p>
14385  * <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
14386  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14387  * <p>
14388  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14389  * source code that is used as the source inside a &lt;script> tag.<br><br>
14390  * <p>
14391  * In order for the browser to process the returned data, the server must wrap the data object
14392  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14393  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14394  * depending on whether the callback name was passed:
14395  * <p>
14396  * <pre><code>
14397 boolean scriptTag = false;
14398 String cb = request.getParameter("callback");
14399 if (cb != null) {
14400     scriptTag = true;
14401     response.setContentType("text/javascript");
14402 } else {
14403     response.setContentType("application/x-json");
14404 }
14405 Writer out = response.getWriter();
14406 if (scriptTag) {
14407     out.write(cb + "(");
14408 }
14409 out.print(dataBlock.toJsonString());
14410 if (scriptTag) {
14411     out.write(");");
14412 }
14413 </pre></code>
14414  *
14415  * @constructor
14416  * @param {Object} config A configuration object.
14417  */
14418 Roo.data.ScriptTagProxy = function(config){
14419     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14420     Roo.apply(this, config);
14421     this.head = document.getElementsByTagName("head")[0];
14422 };
14423
14424 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14425
14426 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14427     /**
14428      * @cfg {String} url The URL from which to request the data object.
14429      */
14430     /**
14431      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14432      */
14433     timeout : 30000,
14434     /**
14435      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14436      * the server the name of the callback function set up by the load call to process the returned data object.
14437      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14438      * javascript output which calls this named function passing the data object as its only parameter.
14439      */
14440     callbackParam : "callback",
14441     /**
14442      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14443      * name to the request.
14444      */
14445     nocache : true,
14446
14447     /**
14448      * Load data from the configured URL, read the data object into
14449      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14450      * process that block using the passed callback.
14451      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14452      * for the request to the remote server.
14453      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14454      * object into a block of Roo.data.Records.
14455      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14456      * The function must be passed <ul>
14457      * <li>The Record block object</li>
14458      * <li>The "arg" argument from the load function</li>
14459      * <li>A boolean success indicator</li>
14460      * </ul>
14461      * @param {Object} scope The scope in which to call the callback
14462      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14463      */
14464     load : function(params, reader, callback, scope, arg){
14465         if(this.fireEvent("beforeload", this, params) !== false){
14466
14467             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14468
14469             var url = this.url;
14470             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14471             if(this.nocache){
14472                 url += "&_dc=" + (new Date().getTime());
14473             }
14474             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14475             var trans = {
14476                 id : transId,
14477                 cb : "stcCallback"+transId,
14478                 scriptId : "stcScript"+transId,
14479                 params : params,
14480                 arg : arg,
14481                 url : url,
14482                 callback : callback,
14483                 scope : scope,
14484                 reader : reader
14485             };
14486             var conn = this;
14487
14488             window[trans.cb] = function(o){
14489                 conn.handleResponse(o, trans);
14490             };
14491
14492             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14493
14494             if(this.autoAbort !== false){
14495                 this.abort();
14496             }
14497
14498             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14499
14500             var script = document.createElement("script");
14501             script.setAttribute("src", url);
14502             script.setAttribute("type", "text/javascript");
14503             script.setAttribute("id", trans.scriptId);
14504             this.head.appendChild(script);
14505
14506             this.trans = trans;
14507         }else{
14508             callback.call(scope||this, null, arg, false);
14509         }
14510     },
14511
14512     // private
14513     isLoading : function(){
14514         return this.trans ? true : false;
14515     },
14516
14517     /**
14518      * Abort the current server request.
14519      */
14520     abort : function(){
14521         if(this.isLoading()){
14522             this.destroyTrans(this.trans);
14523         }
14524     },
14525
14526     // private
14527     destroyTrans : function(trans, isLoaded){
14528         this.head.removeChild(document.getElementById(trans.scriptId));
14529         clearTimeout(trans.timeoutId);
14530         if(isLoaded){
14531             window[trans.cb] = undefined;
14532             try{
14533                 delete window[trans.cb];
14534             }catch(e){}
14535         }else{
14536             // if hasn't been loaded, wait for load to remove it to prevent script error
14537             window[trans.cb] = function(){
14538                 window[trans.cb] = undefined;
14539                 try{
14540                     delete window[trans.cb];
14541                 }catch(e){}
14542             };
14543         }
14544     },
14545
14546     // private
14547     handleResponse : function(o, trans){
14548         this.trans = false;
14549         this.destroyTrans(trans, true);
14550         var result;
14551         try {
14552             result = trans.reader.readRecords(o);
14553         }catch(e){
14554             this.fireEvent("loadexception", this, o, trans.arg, e);
14555             trans.callback.call(trans.scope||window, null, trans.arg, false);
14556             return;
14557         }
14558         this.fireEvent("load", this, o, trans.arg);
14559         trans.callback.call(trans.scope||window, result, trans.arg, true);
14560     },
14561
14562     // private
14563     handleFailure : function(trans){
14564         this.trans = false;
14565         this.destroyTrans(trans, false);
14566         this.fireEvent("loadexception", this, null, trans.arg);
14567         trans.callback.call(trans.scope||window, null, trans.arg, false);
14568     }
14569 });/*
14570  * Based on:
14571  * Ext JS Library 1.1.1
14572  * Copyright(c) 2006-2007, Ext JS, LLC.
14573  *
14574  * Originally Released Under LGPL - original licence link has changed is not relivant.
14575  *
14576  * Fork - LGPL
14577  * <script type="text/javascript">
14578  */
14579
14580 /**
14581  * @class Roo.data.JsonReader
14582  * @extends Roo.data.DataReader
14583  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14584  * based on mappings in a provided Roo.data.Record constructor.
14585  * 
14586  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14587  * in the reply previously. 
14588  * 
14589  * <p>
14590  * Example code:
14591  * <pre><code>
14592 var RecordDef = Roo.data.Record.create([
14593     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14594     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14595 ]);
14596 var myReader = new Roo.data.JsonReader({
14597     totalProperty: "results",    // The property which contains the total dataset size (optional)
14598     root: "rows",                // The property which contains an Array of row objects
14599     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14600 }, RecordDef);
14601 </code></pre>
14602  * <p>
14603  * This would consume a JSON file like this:
14604  * <pre><code>
14605 { 'results': 2, 'rows': [
14606     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14607     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14608 }
14609 </code></pre>
14610  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14611  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14612  * paged from the remote server.
14613  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14614  * @cfg {String} root name of the property which contains the Array of row objects.
14615  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14616  * @cfg {Array} fields Array of field definition objects
14617  * @constructor
14618  * Create a new JsonReader
14619  * @param {Object} meta Metadata configuration options
14620  * @param {Object} recordType Either an Array of field definition objects,
14621  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14622  */
14623 Roo.data.JsonReader = function(meta, recordType){
14624     
14625     meta = meta || {};
14626     // set some defaults:
14627     Roo.applyIf(meta, {
14628         totalProperty: 'total',
14629         successProperty : 'success',
14630         root : 'data',
14631         id : 'id'
14632     });
14633     
14634     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14635 };
14636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14637     
14638     readerType : 'Json',
14639     
14640     /**
14641      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14642      * Used by Store query builder to append _requestMeta to params.
14643      * 
14644      */
14645     metaFromRemote : false,
14646     /**
14647      * This method is only used by a DataProxy which has retrieved data from a remote server.
14648      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14649      * @return {Object} data A data block which is used by an Roo.data.Store object as
14650      * a cache of Roo.data.Records.
14651      */
14652     read : function(response){
14653         var json = response.responseText;
14654        
14655         var o = /* eval:var:o */ eval("("+json+")");
14656         if(!o) {
14657             throw {message: "JsonReader.read: Json object not found"};
14658         }
14659         
14660         if(o.metaData){
14661             
14662             delete this.ef;
14663             this.metaFromRemote = true;
14664             this.meta = o.metaData;
14665             this.recordType = Roo.data.Record.create(o.metaData.fields);
14666             this.onMetaChange(this.meta, this.recordType, o);
14667         }
14668         return this.readRecords(o);
14669     },
14670
14671     // private function a store will implement
14672     onMetaChange : function(meta, recordType, o){
14673
14674     },
14675
14676     /**
14677          * @ignore
14678          */
14679     simpleAccess: function(obj, subsc) {
14680         return obj[subsc];
14681     },
14682
14683         /**
14684          * @ignore
14685          */
14686     getJsonAccessor: function(){
14687         var re = /[\[\.]/;
14688         return function(expr) {
14689             try {
14690                 return(re.test(expr))
14691                     ? new Function("obj", "return obj." + expr)
14692                     : function(obj){
14693                         return obj[expr];
14694                     };
14695             } catch(e){}
14696             return Roo.emptyFn;
14697         };
14698     }(),
14699
14700     /**
14701      * Create a data block containing Roo.data.Records from an XML document.
14702      * @param {Object} o An object which contains an Array of row objects in the property specified
14703      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14704      * which contains the total size of the dataset.
14705      * @return {Object} data A data block which is used by an Roo.data.Store object as
14706      * a cache of Roo.data.Records.
14707      */
14708     readRecords : function(o){
14709         /**
14710          * After any data loads, the raw JSON data is available for further custom processing.
14711          * @type Object
14712          */
14713         this.o = o;
14714         var s = this.meta, Record = this.recordType,
14715             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14716
14717 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14718         if (!this.ef) {
14719             if(s.totalProperty) {
14720                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14721                 }
14722                 if(s.successProperty) {
14723                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14724                 }
14725                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14726                 if (s.id) {
14727                         var g = this.getJsonAccessor(s.id);
14728                         this.getId = function(rec) {
14729                                 var r = g(rec);  
14730                                 return (r === undefined || r === "") ? null : r;
14731                         };
14732                 } else {
14733                         this.getId = function(){return null;};
14734                 }
14735             this.ef = [];
14736             for(var jj = 0; jj < fl; jj++){
14737                 f = fi[jj];
14738                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14739                 this.ef[jj] = this.getJsonAccessor(map);
14740             }
14741         }
14742
14743         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14744         if(s.totalProperty){
14745             var vt = parseInt(this.getTotal(o), 10);
14746             if(!isNaN(vt)){
14747                 totalRecords = vt;
14748             }
14749         }
14750         if(s.successProperty){
14751             var vs = this.getSuccess(o);
14752             if(vs === false || vs === 'false'){
14753                 success = false;
14754             }
14755         }
14756         var records = [];
14757         for(var i = 0; i < c; i++){
14758                 var n = root[i];
14759             var values = {};
14760             var id = this.getId(n);
14761             for(var j = 0; j < fl; j++){
14762                 f = fi[j];
14763             var v = this.ef[j](n);
14764             if (!f.convert) {
14765                 Roo.log('missing convert for ' + f.name);
14766                 Roo.log(f);
14767                 continue;
14768             }
14769             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14770             }
14771             var record = new Record(values, id);
14772             record.json = n;
14773             records[i] = record;
14774         }
14775         return {
14776             raw : o,
14777             success : success,
14778             records : records,
14779             totalRecords : totalRecords
14780         };
14781     },
14782     // used when loading children.. @see loadDataFromChildren
14783     toLoadData: function(rec)
14784     {
14785         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14786         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14787         return { data : data, total : data.length };
14788         
14789     }
14790 });/*
14791  * Based on:
14792  * Ext JS Library 1.1.1
14793  * Copyright(c) 2006-2007, Ext JS, LLC.
14794  *
14795  * Originally Released Under LGPL - original licence link has changed is not relivant.
14796  *
14797  * Fork - LGPL
14798  * <script type="text/javascript">
14799  */
14800
14801 /**
14802  * @class Roo.data.ArrayReader
14803  * @extends Roo.data.DataReader
14804  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14805  * Each element of that Array represents a row of data fields. The
14806  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14807  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14808  * <p>
14809  * Example code:.
14810  * <pre><code>
14811 var RecordDef = Roo.data.Record.create([
14812     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14813     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14814 ]);
14815 var myReader = new Roo.data.ArrayReader({
14816     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14817 }, RecordDef);
14818 </code></pre>
14819  * <p>
14820  * This would consume an Array like this:
14821  * <pre><code>
14822 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14823   </code></pre>
14824  
14825  * @constructor
14826  * Create a new JsonReader
14827  * @param {Object} meta Metadata configuration options.
14828  * @param {Object|Array} recordType Either an Array of field definition objects
14829  * 
14830  * @cfg {Array} fields Array of field definition objects
14831  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14832  * as specified to {@link Roo.data.Record#create},
14833  * or an {@link Roo.data.Record} object
14834  *
14835  * 
14836  * created using {@link Roo.data.Record#create}.
14837  */
14838 Roo.data.ArrayReader = function(meta, recordType)
14839 {    
14840     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14841 };
14842
14843 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14844     
14845       /**
14846      * Create a data block containing Roo.data.Records from an XML document.
14847      * @param {Object} o An Array of row objects which represents the dataset.
14848      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14849      * a cache of Roo.data.Records.
14850      */
14851     readRecords : function(o)
14852     {
14853         var sid = this.meta ? this.meta.id : null;
14854         var recordType = this.recordType, fields = recordType.prototype.fields;
14855         var records = [];
14856         var root = o;
14857         for(var i = 0; i < root.length; i++){
14858                 var n = root[i];
14859             var values = {};
14860             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14861             for(var j = 0, jlen = fields.length; j < jlen; j++){
14862                 var f = fields.items[j];
14863                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14864                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14865                 v = f.convert(v);
14866                 values[f.name] = v;
14867             }
14868             var record = new recordType(values, id);
14869             record.json = n;
14870             records[records.length] = record;
14871         }
14872         return {
14873             records : records,
14874             totalRecords : records.length
14875         };
14876     },
14877     // used when loading children.. @see loadDataFromChildren
14878     toLoadData: function(rec)
14879     {
14880         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14881         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14882         
14883     }
14884     
14885     
14886 });/*
14887  * - LGPL
14888  * * 
14889  */
14890
14891 /**
14892  * @class Roo.bootstrap.ComboBox
14893  * @extends Roo.bootstrap.TriggerField
14894  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14895  * @cfg {Boolean} append (true|false) default false
14896  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14897  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14898  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14899  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14900  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14901  * @cfg {Boolean} animate default true
14902  * @cfg {Boolean} emptyResultText only for touch device
14903  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14904  * @cfg {String} emptyTitle default ''
14905  * @cfg {Number} width fixed with? experimental
14906  * @constructor
14907  * Create a new ComboBox.
14908  * @param {Object} config Configuration options
14909  */
14910 Roo.bootstrap.ComboBox = function(config){
14911     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14912     this.addEvents({
14913         /**
14914          * @event expand
14915          * Fires when the dropdown list is expanded
14916         * @param {Roo.bootstrap.ComboBox} combo This combo box
14917         */
14918         'expand' : true,
14919         /**
14920          * @event collapse
14921          * Fires when the dropdown list is collapsed
14922         * @param {Roo.bootstrap.ComboBox} combo This combo box
14923         */
14924         'collapse' : true,
14925         /**
14926          * @event beforeselect
14927          * Fires before a list item is selected. Return false to cancel the selection.
14928         * @param {Roo.bootstrap.ComboBox} combo This combo box
14929         * @param {Roo.data.Record} record The data record returned from the underlying store
14930         * @param {Number} index The index of the selected item in the dropdown list
14931         */
14932         'beforeselect' : true,
14933         /**
14934          * @event select
14935          * Fires when a list item is selected
14936         * @param {Roo.bootstrap.ComboBox} combo This combo box
14937         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14938         * @param {Number} index The index of the selected item in the dropdown list
14939         */
14940         'select' : true,
14941         /**
14942          * @event beforequery
14943          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14944          * The event object passed has these properties:
14945         * @param {Roo.bootstrap.ComboBox} combo This combo box
14946         * @param {String} query The query
14947         * @param {Boolean} forceAll true to force "all" query
14948         * @param {Boolean} cancel true to cancel the query
14949         * @param {Object} e The query event object
14950         */
14951         'beforequery': true,
14952          /**
14953          * @event add
14954          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14955         * @param {Roo.bootstrap.ComboBox} combo This combo box
14956         */
14957         'add' : true,
14958         /**
14959          * @event edit
14960          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14961         * @param {Roo.bootstrap.ComboBox} combo This combo box
14962         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14963         */
14964         'edit' : true,
14965         /**
14966          * @event remove
14967          * Fires when the remove value from the combobox array
14968         * @param {Roo.bootstrap.ComboBox} combo This combo box
14969         */
14970         'remove' : true,
14971         /**
14972          * @event afterremove
14973          * Fires when the remove value from the combobox array
14974         * @param {Roo.bootstrap.ComboBox} combo This combo box
14975         */
14976         'afterremove' : true,
14977         /**
14978          * @event specialfilter
14979          * Fires when specialfilter
14980             * @param {Roo.bootstrap.ComboBox} combo This combo box
14981             */
14982         'specialfilter' : true,
14983         /**
14984          * @event tick
14985          * Fires when tick the element
14986             * @param {Roo.bootstrap.ComboBox} combo This combo box
14987             */
14988         'tick' : true,
14989         /**
14990          * @event touchviewdisplay
14991          * Fires when touch view require special display (default is using displayField)
14992             * @param {Roo.bootstrap.ComboBox} combo This combo box
14993             * @param {Object} cfg set html .
14994             */
14995         'touchviewdisplay' : true
14996         
14997     });
14998     
14999     this.item = [];
15000     this.tickItems = [];
15001     
15002     this.selectedIndex = -1;
15003     if(this.mode == 'local'){
15004         if(config.queryDelay === undefined){
15005             this.queryDelay = 10;
15006         }
15007         if(config.minChars === undefined){
15008             this.minChars = 0;
15009         }
15010     }
15011 };
15012
15013 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15014      
15015     /**
15016      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15017      * rendering into an Roo.Editor, defaults to false)
15018      */
15019     /**
15020      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15021      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15022      */
15023     /**
15024      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15025      */
15026     /**
15027      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15028      * the dropdown list (defaults to undefined, with no header element)
15029      */
15030
15031      /**
15032      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15033      */
15034      
15035      /**
15036      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15037      */
15038     listWidth: undefined,
15039     /**
15040      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15041      * mode = 'remote' or 'text' if mode = 'local')
15042      */
15043     displayField: undefined,
15044     
15045     /**
15046      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15047      * mode = 'remote' or 'value' if mode = 'local'). 
15048      * Note: use of a valueField requires the user make a selection
15049      * in order for a value to be mapped.
15050      */
15051     valueField: undefined,
15052     /**
15053      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15054      */
15055     modalTitle : '',
15056     
15057     /**
15058      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15059      * field's data value (defaults to the underlying DOM element's name)
15060      */
15061     hiddenName: undefined,
15062     /**
15063      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15064      */
15065     listClass: '',
15066     /**
15067      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15068      */
15069     selectedClass: 'active',
15070     
15071     /**
15072      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15073      */
15074     shadow:'sides',
15075     /**
15076      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15077      * anchor positions (defaults to 'tl-bl')
15078      */
15079     listAlign: 'tl-bl?',
15080     /**
15081      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15082      */
15083     maxHeight: 300,
15084     /**
15085      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15086      * query specified by the allQuery config option (defaults to 'query')
15087      */
15088     triggerAction: 'query',
15089     /**
15090      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15091      * (defaults to 4, does not apply if editable = false)
15092      */
15093     minChars : 4,
15094     /**
15095      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15096      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15097      */
15098     typeAhead: false,
15099     /**
15100      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15101      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15102      */
15103     queryDelay: 500,
15104     /**
15105      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15106      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15107      */
15108     pageSize: 0,
15109     /**
15110      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15111      * when editable = true (defaults to false)
15112      */
15113     selectOnFocus:false,
15114     /**
15115      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15116      */
15117     queryParam: 'query',
15118     /**
15119      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15120      * when mode = 'remote' (defaults to 'Loading...')
15121      */
15122     loadingText: 'Loading...',
15123     /**
15124      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15125      */
15126     resizable: false,
15127     /**
15128      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15129      */
15130     handleHeight : 8,
15131     /**
15132      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15133      * traditional select (defaults to true)
15134      */
15135     editable: true,
15136     /**
15137      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15138      */
15139     allQuery: '',
15140     /**
15141      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15142      */
15143     mode: 'remote',
15144     /**
15145      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15146      * listWidth has a higher value)
15147      */
15148     minListWidth : 70,
15149     /**
15150      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15151      * allow the user to set arbitrary text into the field (defaults to false)
15152      */
15153     forceSelection:false,
15154     /**
15155      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15156      * if typeAhead = true (defaults to 250)
15157      */
15158     typeAheadDelay : 250,
15159     /**
15160      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15161      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15162      */
15163     valueNotFoundText : undefined,
15164     /**
15165      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15166      */
15167     blockFocus : false,
15168     
15169     /**
15170      * @cfg {Boolean} disableClear Disable showing of clear button.
15171      */
15172     disableClear : false,
15173     /**
15174      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15175      */
15176     alwaysQuery : false,
15177     
15178     /**
15179      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15180      */
15181     multiple : false,
15182     
15183     /**
15184      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15185      */
15186     invalidClass : "has-warning",
15187     
15188     /**
15189      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15190      */
15191     validClass : "has-success",
15192     
15193     /**
15194      * @cfg {Boolean} specialFilter (true|false) special filter default false
15195      */
15196     specialFilter : false,
15197     
15198     /**
15199      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15200      */
15201     mobileTouchView : true,
15202     
15203     /**
15204      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15205      */
15206     useNativeIOS : false,
15207     
15208     /**
15209      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15210      */
15211     mobile_restrict_height : false,
15212     
15213     ios_options : false,
15214     
15215     //private
15216     addicon : false,
15217     editicon: false,
15218     
15219     page: 0,
15220     hasQuery: false,
15221     append: false,
15222     loadNext: false,
15223     autoFocus : true,
15224     tickable : false,
15225     btnPosition : 'right',
15226     triggerList : true,
15227     showToggleBtn : true,
15228     animate : true,
15229     emptyResultText: 'Empty',
15230     triggerText : 'Select',
15231     emptyTitle : '',
15232     width : false,
15233     
15234     // element that contains real text value.. (when hidden is used..)
15235     
15236     getAutoCreate : function()
15237     {   
15238         var cfg = false;
15239         //render
15240         /*
15241          * Render classic select for iso
15242          */
15243         
15244         if(Roo.isIOS && this.useNativeIOS){
15245             cfg = this.getAutoCreateNativeIOS();
15246             return cfg;
15247         }
15248         
15249         /*
15250          * Touch Devices
15251          */
15252         
15253         if(Roo.isTouch && this.mobileTouchView){
15254             cfg = this.getAutoCreateTouchView();
15255             return cfg;;
15256         }
15257         
15258         /*
15259          *  Normal ComboBox
15260          */
15261         if(!this.tickable){
15262             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15263             return cfg;
15264         }
15265         
15266         /*
15267          *  ComboBox with tickable selections
15268          */
15269              
15270         var align = this.labelAlign || this.parentLabelAlign();
15271         
15272         cfg = {
15273             cls : 'form-group roo-combobox-tickable' //input-group
15274         };
15275         
15276         var btn_text_select = '';
15277         var btn_text_done = '';
15278         var btn_text_cancel = '';
15279         
15280         if (this.btn_text_show) {
15281             btn_text_select = 'Select';
15282             btn_text_done = 'Done';
15283             btn_text_cancel = 'Cancel'; 
15284         }
15285         
15286         var buttons = {
15287             tag : 'div',
15288             cls : 'tickable-buttons',
15289             cn : [
15290                 {
15291                     tag : 'button',
15292                     type : 'button',
15293                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15294                     //html : this.triggerText
15295                     html: btn_text_select
15296                 },
15297                 {
15298                     tag : 'button',
15299                     type : 'button',
15300                     name : 'ok',
15301                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15302                     //html : 'Done'
15303                     html: btn_text_done
15304                 },
15305                 {
15306                     tag : 'button',
15307                     type : 'button',
15308                     name : 'cancel',
15309                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15310                     //html : 'Cancel'
15311                     html: btn_text_cancel
15312                 }
15313             ]
15314         };
15315         
15316         if(this.editable){
15317             buttons.cn.unshift({
15318                 tag: 'input',
15319                 cls: 'roo-select2-search-field-input'
15320             });
15321         }
15322         
15323         var _this = this;
15324         
15325         Roo.each(buttons.cn, function(c){
15326             if (_this.size) {
15327                 c.cls += ' btn-' + _this.size;
15328             }
15329
15330             if (_this.disabled) {
15331                 c.disabled = true;
15332             }
15333         });
15334         
15335         var box = {
15336             tag: 'div',
15337             style : 'display: contents',
15338             cn: [
15339                 {
15340                     tag: 'input',
15341                     type : 'hidden',
15342                     cls: 'form-hidden-field'
15343                 },
15344                 {
15345                     tag: 'ul',
15346                     cls: 'roo-select2-choices',
15347                     cn:[
15348                         {
15349                             tag: 'li',
15350                             cls: 'roo-select2-search-field',
15351                             cn: [
15352                                 buttons
15353                             ]
15354                         }
15355                     ]
15356                 }
15357             ]
15358         };
15359         
15360         var combobox = {
15361             cls: 'roo-select2-container input-group roo-select2-container-multi',
15362             cn: [
15363                 
15364                 box
15365 //                {
15366 //                    tag: 'ul',
15367 //                    cls: 'typeahead typeahead-long dropdown-menu',
15368 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15369 //                }
15370             ]
15371         };
15372         
15373         if(this.hasFeedback && !this.allowBlank){
15374             
15375             var feedback = {
15376                 tag: 'span',
15377                 cls: 'glyphicon form-control-feedback'
15378             };
15379
15380             combobox.cn.push(feedback);
15381         }
15382         
15383         
15384         
15385         var indicator = {
15386             tag : 'i',
15387             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15388             tooltip : 'This field is required'
15389         };
15390         if (Roo.bootstrap.version == 4) {
15391             indicator = {
15392                 tag : 'i',
15393                 style : 'display:none'
15394             };
15395         }
15396         if (align ==='left' && this.fieldLabel.length) {
15397             
15398             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15399             
15400             cfg.cn = [
15401                 indicator,
15402                 {
15403                     tag: 'label',
15404                     'for' :  id,
15405                     cls : 'control-label col-form-label',
15406                     html : this.fieldLabel
15407
15408                 },
15409                 {
15410                     cls : "", 
15411                     cn: [
15412                         combobox
15413                     ]
15414                 }
15415
15416             ];
15417             
15418             var labelCfg = cfg.cn[1];
15419             var contentCfg = cfg.cn[2];
15420             
15421
15422             if(this.indicatorpos == 'right'){
15423                 
15424                 cfg.cn = [
15425                     {
15426                         tag: 'label',
15427                         'for' :  id,
15428                         cls : 'control-label col-form-label',
15429                         cn : [
15430                             {
15431                                 tag : 'span',
15432                                 html : this.fieldLabel
15433                             },
15434                             indicator
15435                         ]
15436                     },
15437                     {
15438                         cls : "",
15439                         cn: [
15440                             combobox
15441                         ]
15442                     }
15443
15444                 ];
15445                 
15446                 
15447                 
15448                 labelCfg = cfg.cn[0];
15449                 contentCfg = cfg.cn[1];
15450             
15451             }
15452             
15453             if(this.labelWidth > 12){
15454                 labelCfg.style = "width: " + this.labelWidth + 'px';
15455             }
15456             if(this.width * 1 > 0){
15457                 contentCfg.style = "width: " + this.width + 'px';
15458             }
15459             if(this.labelWidth < 13 && this.labelmd == 0){
15460                 this.labelmd = this.labelWidth;
15461             }
15462             
15463             if(this.labellg > 0){
15464                 labelCfg.cls += ' col-lg-' + this.labellg;
15465                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15466             }
15467             
15468             if(this.labelmd > 0){
15469                 labelCfg.cls += ' col-md-' + this.labelmd;
15470                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15471             }
15472             
15473             if(this.labelsm > 0){
15474                 labelCfg.cls += ' col-sm-' + this.labelsm;
15475                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15476             }
15477             
15478             if(this.labelxs > 0){
15479                 labelCfg.cls += ' col-xs-' + this.labelxs;
15480                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15481             }
15482                 
15483                 
15484         } else if ( this.fieldLabel.length) {
15485 //                Roo.log(" label");
15486                  cfg.cn = [
15487                    indicator,
15488                     {
15489                         tag: 'label',
15490                         //cls : 'input-group-addon',
15491                         html : this.fieldLabel
15492                     },
15493                     combobox
15494                 ];
15495                 
15496                 if(this.indicatorpos == 'right'){
15497                     cfg.cn = [
15498                         {
15499                             tag: 'label',
15500                             //cls : 'input-group-addon',
15501                             html : this.fieldLabel
15502                         },
15503                         indicator,
15504                         combobox
15505                     ];
15506                     
15507                 }
15508
15509         } else {
15510             
15511 //                Roo.log(" no label && no align");
15512                 cfg = combobox
15513                      
15514                 
15515         }
15516          
15517         var settings=this;
15518         ['xs','sm','md','lg'].map(function(size){
15519             if (settings[size]) {
15520                 cfg.cls += ' col-' + size + '-' + settings[size];
15521             }
15522         });
15523         
15524         return cfg;
15525         
15526     },
15527     
15528     _initEventsCalled : false,
15529     
15530     // private
15531     initEvents: function()
15532     {   
15533         if (this._initEventsCalled) { // as we call render... prevent looping...
15534             return;
15535         }
15536         this._initEventsCalled = true;
15537         
15538         if (!this.store) {
15539             throw "can not find store for combo";
15540         }
15541         
15542         this.indicator = this.indicatorEl();
15543         
15544         this.store = Roo.factory(this.store, Roo.data);
15545         this.store.parent = this;
15546         
15547         // if we are building from html. then this element is so complex, that we can not really
15548         // use the rendered HTML.
15549         // so we have to trash and replace the previous code.
15550         if (Roo.XComponent.build_from_html) {
15551             // remove this element....
15552             var e = this.el.dom, k=0;
15553             while (e ) { e = e.previousSibling;  ++k;}
15554
15555             this.el.remove();
15556             
15557             this.el=false;
15558             this.rendered = false;
15559             
15560             this.render(this.parent().getChildContainer(true), k);
15561         }
15562         
15563         if(Roo.isIOS && this.useNativeIOS){
15564             this.initIOSView();
15565             return;
15566         }
15567         
15568         /*
15569          * Touch Devices
15570          */
15571         
15572         if(Roo.isTouch && this.mobileTouchView){
15573             this.initTouchView();
15574             return;
15575         }
15576         
15577         if(this.tickable){
15578             this.initTickableEvents();
15579             return;
15580         }
15581         
15582         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15583         
15584         if(this.hiddenName){
15585             
15586             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15587             
15588             this.hiddenField.dom.value =
15589                 this.hiddenValue !== undefined ? this.hiddenValue :
15590                 this.value !== undefined ? this.value : '';
15591
15592             // prevent input submission
15593             this.el.dom.removeAttribute('name');
15594             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15595              
15596              
15597         }
15598         //if(Roo.isGecko){
15599         //    this.el.dom.setAttribute('autocomplete', 'off');
15600         //}
15601         
15602         var cls = 'x-combo-list';
15603         
15604         //this.list = new Roo.Layer({
15605         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15606         //});
15607         
15608         var _this = this;
15609         
15610         (function(){
15611             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15612             _this.list.setWidth(lw);
15613         }).defer(100);
15614         
15615         this.list.on('mouseover', this.onViewOver, this);
15616         this.list.on('mousemove', this.onViewMove, this);
15617         this.list.on('scroll', this.onViewScroll, this);
15618         
15619         /*
15620         this.list.swallowEvent('mousewheel');
15621         this.assetHeight = 0;
15622
15623         if(this.title){
15624             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15625             this.assetHeight += this.header.getHeight();
15626         }
15627
15628         this.innerList = this.list.createChild({cls:cls+'-inner'});
15629         this.innerList.on('mouseover', this.onViewOver, this);
15630         this.innerList.on('mousemove', this.onViewMove, this);
15631         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15632         
15633         if(this.allowBlank && !this.pageSize && !this.disableClear){
15634             this.footer = this.list.createChild({cls:cls+'-ft'});
15635             this.pageTb = new Roo.Toolbar(this.footer);
15636            
15637         }
15638         if(this.pageSize){
15639             this.footer = this.list.createChild({cls:cls+'-ft'});
15640             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15641                     {pageSize: this.pageSize});
15642             
15643         }
15644         
15645         if (this.pageTb && this.allowBlank && !this.disableClear) {
15646             var _this = this;
15647             this.pageTb.add(new Roo.Toolbar.Fill(), {
15648                 cls: 'x-btn-icon x-btn-clear',
15649                 text: '&#160;',
15650                 handler: function()
15651                 {
15652                     _this.collapse();
15653                     _this.clearValue();
15654                     _this.onSelect(false, -1);
15655                 }
15656             });
15657         }
15658         if (this.footer) {
15659             this.assetHeight += this.footer.getHeight();
15660         }
15661         */
15662             
15663         if(!this.tpl){
15664             this.tpl = Roo.bootstrap.version == 4 ?
15665                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15666                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15667         }
15668
15669         this.view = new Roo.View(this.list, this.tpl, {
15670             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15671         });
15672         //this.view.wrapEl.setDisplayed(false);
15673         this.view.on('click', this.onViewClick, this);
15674         
15675         
15676         this.store.on('beforeload', this.onBeforeLoad, this);
15677         this.store.on('load', this.onLoad, this);
15678         this.store.on('loadexception', this.onLoadException, this);
15679         /*
15680         if(this.resizable){
15681             this.resizer = new Roo.Resizable(this.list,  {
15682                pinned:true, handles:'se'
15683             });
15684             this.resizer.on('resize', function(r, w, h){
15685                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15686                 this.listWidth = w;
15687                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15688                 this.restrictHeight();
15689             }, this);
15690             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15691         }
15692         */
15693         if(!this.editable){
15694             this.editable = true;
15695             this.setEditable(false);
15696         }
15697         
15698         /*
15699         
15700         if (typeof(this.events.add.listeners) != 'undefined') {
15701             
15702             this.addicon = this.wrap.createChild(
15703                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15704        
15705             this.addicon.on('click', function(e) {
15706                 this.fireEvent('add', this);
15707             }, this);
15708         }
15709         if (typeof(this.events.edit.listeners) != 'undefined') {
15710             
15711             this.editicon = this.wrap.createChild(
15712                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15713             if (this.addicon) {
15714                 this.editicon.setStyle('margin-left', '40px');
15715             }
15716             this.editicon.on('click', function(e) {
15717                 
15718                 // we fire even  if inothing is selected..
15719                 this.fireEvent('edit', this, this.lastData );
15720                 
15721             }, this);
15722         }
15723         */
15724         
15725         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15726             "up" : function(e){
15727                 this.inKeyMode = true;
15728                 this.selectPrev();
15729             },
15730
15731             "down" : function(e){
15732                 if(!this.isExpanded()){
15733                     this.onTriggerClick();
15734                 }else{
15735                     this.inKeyMode = true;
15736                     this.selectNext();
15737                 }
15738             },
15739
15740             "enter" : function(e){
15741 //                this.onViewClick();
15742                 //return true;
15743                 this.collapse();
15744                 
15745                 if(this.fireEvent("specialkey", this, e)){
15746                     this.onViewClick(false);
15747                 }
15748                 
15749                 return true;
15750             },
15751
15752             "esc" : function(e){
15753                 this.collapse();
15754             },
15755
15756             "tab" : function(e){
15757                 this.collapse();
15758                 
15759                 if(this.fireEvent("specialkey", this, e)){
15760                     this.onViewClick(false);
15761                 }
15762                 
15763                 return true;
15764             },
15765
15766             scope : this,
15767
15768             doRelay : function(foo, bar, hname){
15769                 if(hname == 'down' || this.scope.isExpanded()){
15770                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15771                 }
15772                 return true;
15773             },
15774
15775             forceKeyDown: true
15776         });
15777         
15778         
15779         this.queryDelay = Math.max(this.queryDelay || 10,
15780                 this.mode == 'local' ? 10 : 250);
15781         
15782         
15783         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15784         
15785         if(this.typeAhead){
15786             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15787         }
15788         if(this.editable !== false){
15789             this.inputEl().on("keyup", this.onKeyUp, this);
15790         }
15791         if(this.forceSelection){
15792             this.inputEl().on('blur', this.doForce, this);
15793         }
15794         
15795         if(this.multiple){
15796             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15797             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15798         }
15799     },
15800     
15801     initTickableEvents: function()
15802     {   
15803         this.createList();
15804         
15805         if(this.hiddenName){
15806             
15807             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15808             
15809             this.hiddenField.dom.value =
15810                 this.hiddenValue !== undefined ? this.hiddenValue :
15811                 this.value !== undefined ? this.value : '';
15812
15813             // prevent input submission
15814             this.el.dom.removeAttribute('name');
15815             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15816              
15817              
15818         }
15819         
15820 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15821         
15822         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15824         if(this.triggerList){
15825             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15826         }
15827          
15828         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15829         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15830         
15831         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15832         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15833         
15834         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15835         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15836         
15837         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15838         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15839         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15840         
15841         this.okBtn.hide();
15842         this.cancelBtn.hide();
15843         
15844         var _this = this;
15845         
15846         (function(){
15847             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15848             _this.list.setWidth(lw);
15849         }).defer(100);
15850         
15851         this.list.on('mouseover', this.onViewOver, this);
15852         this.list.on('mousemove', this.onViewMove, this);
15853         
15854         this.list.on('scroll', this.onViewScroll, this);
15855         
15856         if(!this.tpl){
15857             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15858                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15859         }
15860
15861         this.view = new Roo.View(this.list, this.tpl, {
15862             singleSelect:true,
15863             tickable:true,
15864             parent:this,
15865             store: this.store,
15866             selectedClass: this.selectedClass
15867         });
15868         
15869         //this.view.wrapEl.setDisplayed(false);
15870         this.view.on('click', this.onViewClick, this);
15871         
15872         
15873         
15874         this.store.on('beforeload', this.onBeforeLoad, this);
15875         this.store.on('load', this.onLoad, this);
15876         this.store.on('loadexception', this.onLoadException, this);
15877         
15878         if(this.editable){
15879             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15880                 "up" : function(e){
15881                     this.inKeyMode = true;
15882                     this.selectPrev();
15883                 },
15884
15885                 "down" : function(e){
15886                     this.inKeyMode = true;
15887                     this.selectNext();
15888                 },
15889
15890                 "enter" : function(e){
15891                     if(this.fireEvent("specialkey", this, e)){
15892                         this.onViewClick(false);
15893                     }
15894                     
15895                     return true;
15896                 },
15897
15898                 "esc" : function(e){
15899                     this.onTickableFooterButtonClick(e, false, false);
15900                 },
15901
15902                 "tab" : function(e){
15903                     this.fireEvent("specialkey", this, e);
15904                     
15905                     this.onTickableFooterButtonClick(e, false, false);
15906                     
15907                     return true;
15908                 },
15909
15910                 scope : this,
15911
15912                 doRelay : function(e, fn, key){
15913                     if(this.scope.isExpanded()){
15914                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15915                     }
15916                     return true;
15917                 },
15918
15919                 forceKeyDown: true
15920             });
15921         }
15922         
15923         this.queryDelay = Math.max(this.queryDelay || 10,
15924                 this.mode == 'local' ? 10 : 250);
15925         
15926         
15927         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15928         
15929         if(this.typeAhead){
15930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15931         }
15932         
15933         if(this.editable !== false){
15934             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15935         }
15936         
15937         this.indicator = this.indicatorEl();
15938         
15939         if(this.indicator){
15940             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15941             this.indicator.hide();
15942         }
15943         
15944     },
15945
15946     onDestroy : function(){
15947         if(this.view){
15948             this.view.setStore(null);
15949             this.view.el.removeAllListeners();
15950             this.view.el.remove();
15951             this.view.purgeListeners();
15952         }
15953         if(this.list){
15954             this.list.dom.innerHTML  = '';
15955         }
15956         
15957         if(this.store){
15958             this.store.un('beforeload', this.onBeforeLoad, this);
15959             this.store.un('load', this.onLoad, this);
15960             this.store.un('loadexception', this.onLoadException, this);
15961         }
15962         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15963     },
15964
15965     // private
15966     fireKey : function(e){
15967         if(e.isNavKeyPress() && !this.list.isVisible()){
15968             this.fireEvent("specialkey", this, e);
15969         }
15970     },
15971
15972     // private
15973     onResize: function(w, h)
15974     {
15975         
15976         
15977 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15978 //        
15979 //        if(typeof w != 'number'){
15980 //            // we do not handle it!?!?
15981 //            return;
15982 //        }
15983 //        var tw = this.trigger.getWidth();
15984 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15985 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15986 //        var x = w - tw;
15987 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15988 //            
15989 //        //this.trigger.setStyle('left', x+'px');
15990 //        
15991 //        if(this.list && this.listWidth === undefined){
15992 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15993 //            this.list.setWidth(lw);
15994 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15995 //        }
15996         
15997     
15998         
15999     },
16000
16001     /**
16002      * Allow or prevent the user from directly editing the field text.  If false is passed,
16003      * the user will only be able to select from the items defined in the dropdown list.  This method
16004      * is the runtime equivalent of setting the 'editable' config option at config time.
16005      * @param {Boolean} value True to allow the user to directly edit the field text
16006      */
16007     setEditable : function(value){
16008         if(value == this.editable){
16009             return;
16010         }
16011         this.editable = value;
16012         if(!value){
16013             this.inputEl().dom.setAttribute('readOnly', true);
16014             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16015             this.inputEl().addClass('x-combo-noedit');
16016         }else{
16017             this.inputEl().dom.setAttribute('readOnly', false);
16018             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16019             this.inputEl().removeClass('x-combo-noedit');
16020         }
16021     },
16022
16023     // private
16024     
16025     onBeforeLoad : function(combo,opts){
16026         if(!this.hasFocus){
16027             return;
16028         }
16029          if (!opts.add) {
16030             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16031          }
16032         this.restrictHeight();
16033         this.selectedIndex = -1;
16034     },
16035
16036     // private
16037     onLoad : function(){
16038         
16039         this.hasQuery = false;
16040         
16041         if(!this.hasFocus){
16042             return;
16043         }
16044         
16045         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16046             this.loading.hide();
16047         }
16048         
16049         if(this.store.getCount() > 0){
16050             
16051             this.expand();
16052             this.restrictHeight();
16053             if(this.lastQuery == this.allQuery){
16054                 if(this.editable && !this.tickable){
16055                     this.inputEl().dom.select();
16056                 }
16057                 
16058                 if(
16059                     !this.selectByValue(this.value, true) &&
16060                     this.autoFocus && 
16061                     (
16062                         !this.store.lastOptions ||
16063                         typeof(this.store.lastOptions.add) == 'undefined' || 
16064                         this.store.lastOptions.add != true
16065                     )
16066                 ){
16067                     this.select(0, true);
16068                 }
16069             }else{
16070                 if(this.autoFocus){
16071                     this.selectNext();
16072                 }
16073                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16074                     this.taTask.delay(this.typeAheadDelay);
16075                 }
16076             }
16077         }else{
16078             this.onEmptyResults();
16079         }
16080         
16081         //this.el.focus();
16082     },
16083     // private
16084     onLoadException : function()
16085     {
16086         this.hasQuery = false;
16087         
16088         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16089             this.loading.hide();
16090         }
16091         
16092         if(this.tickable && this.editable){
16093             return;
16094         }
16095         
16096         this.collapse();
16097         // only causes errors at present
16098         //Roo.log(this.store.reader.jsonData);
16099         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16100             // fixme
16101             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16102         //}
16103         
16104         
16105     },
16106     // private
16107     onTypeAhead : function(){
16108         if(this.store.getCount() > 0){
16109             var r = this.store.getAt(0);
16110             var newValue = r.data[this.displayField];
16111             var len = newValue.length;
16112             var selStart = this.getRawValue().length;
16113             
16114             if(selStart != len){
16115                 this.setRawValue(newValue);
16116                 this.selectText(selStart, newValue.length);
16117             }
16118         }
16119     },
16120
16121     // private
16122     onSelect : function(record, index){
16123         
16124         if(this.fireEvent('beforeselect', this, record, index) !== false){
16125         
16126             this.setFromData(index > -1 ? record.data : false);
16127             
16128             this.collapse();
16129             this.fireEvent('select', this, record, index);
16130         }
16131     },
16132
16133     /**
16134      * Returns the currently selected field value or empty string if no value is set.
16135      * @return {String} value The selected value
16136      */
16137     getValue : function()
16138     {
16139         if(Roo.isIOS && this.useNativeIOS){
16140             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16141         }
16142         
16143         if(this.multiple){
16144             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16145         }
16146         
16147         if(this.valueField){
16148             return typeof this.value != 'undefined' ? this.value : '';
16149         }else{
16150             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16151         }
16152     },
16153     
16154     getRawValue : function()
16155     {
16156         if(Roo.isIOS && this.useNativeIOS){
16157             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16158         }
16159         
16160         var v = this.inputEl().getValue();
16161         
16162         return v;
16163     },
16164
16165     /**
16166      * Clears any text/value currently set in the field
16167      */
16168     clearValue : function(){
16169         
16170         if(this.hiddenField){
16171             this.hiddenField.dom.value = '';
16172         }
16173         this.value = '';
16174         this.setRawValue('');
16175         this.lastSelectionText = '';
16176         this.lastData = false;
16177         
16178         var close = this.closeTriggerEl();
16179         
16180         if(close){
16181             close.hide();
16182         }
16183         
16184         this.validate();
16185         
16186     },
16187
16188     /**
16189      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16190      * will be displayed in the field.  If the value does not match the data value of an existing item,
16191      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16192      * Otherwise the field will be blank (although the value will still be set).
16193      * @param {String} value The value to match
16194      */
16195     setValue : function(v)
16196     {
16197         if(Roo.isIOS && this.useNativeIOS){
16198             this.setIOSValue(v);
16199             return;
16200         }
16201         
16202         if(this.multiple){
16203             this.syncValue();
16204             return;
16205         }
16206         
16207         var text = v;
16208         if(this.valueField){
16209             var r = this.findRecord(this.valueField, v);
16210             if(r){
16211                 text = r.data[this.displayField];
16212             }else if(this.valueNotFoundText !== undefined){
16213                 text = this.valueNotFoundText;
16214             }
16215         }
16216         this.lastSelectionText = text;
16217         if(this.hiddenField){
16218             this.hiddenField.dom.value = v;
16219         }
16220         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16221         this.value = v;
16222         
16223         var close = this.closeTriggerEl();
16224         
16225         if(close){
16226             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16227         }
16228         
16229         this.validate();
16230     },
16231     /**
16232      * @property {Object} the last set data for the element
16233      */
16234     
16235     lastData : false,
16236     /**
16237      * Sets the value of the field based on a object which is related to the record format for the store.
16238      * @param {Object} value the value to set as. or false on reset?
16239      */
16240     setFromData : function(o){
16241         
16242         if(this.multiple){
16243             this.addItem(o);
16244             return;
16245         }
16246             
16247         var dv = ''; // display value
16248         var vv = ''; // value value..
16249         this.lastData = o;
16250         if (this.displayField) {
16251             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16252         } else {
16253             // this is an error condition!!!
16254             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16255         }
16256         
16257         if(this.valueField){
16258             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16259         }
16260         
16261         var close = this.closeTriggerEl();
16262         
16263         if(close){
16264             if(dv.length || vv * 1 > 0){
16265                 close.show() ;
16266                 this.blockFocus=true;
16267             } else {
16268                 close.hide();
16269             }             
16270         }
16271         
16272         if(this.hiddenField){
16273             this.hiddenField.dom.value = vv;
16274             
16275             this.lastSelectionText = dv;
16276             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16277             this.value = vv;
16278             return;
16279         }
16280         // no hidden field.. - we store the value in 'value', but still display
16281         // display field!!!!
16282         this.lastSelectionText = dv;
16283         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16284         this.value = vv;
16285         
16286         
16287         
16288     },
16289     // private
16290     reset : function(){
16291         // overridden so that last data is reset..
16292         
16293         if(this.multiple){
16294             this.clearItem();
16295             return;
16296         }
16297         
16298         this.setValue(this.originalValue);
16299         //this.clearInvalid();
16300         this.lastData = false;
16301         if (this.view) {
16302             this.view.clearSelections();
16303         }
16304         
16305         this.validate();
16306     },
16307     // private
16308     findRecord : function(prop, value){
16309         var record;
16310         if(this.store.getCount() > 0){
16311             this.store.each(function(r){
16312                 if(r.data[prop] == value){
16313                     record = r;
16314                     return false;
16315                 }
16316                 return true;
16317             });
16318         }
16319         return record;
16320     },
16321     
16322     getName: function()
16323     {
16324         // returns hidden if it's set..
16325         if (!this.rendered) {return ''};
16326         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16327         
16328     },
16329     // private
16330     onViewMove : function(e, t){
16331         this.inKeyMode = false;
16332     },
16333
16334     // private
16335     onViewOver : function(e, t){
16336         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16337             return;
16338         }
16339         var item = this.view.findItemFromChild(t);
16340         
16341         if(item){
16342             var index = this.view.indexOf(item);
16343             this.select(index, false);
16344         }
16345     },
16346
16347     // private
16348     onViewClick : function(view, doFocus, el, e)
16349     {
16350         var index = this.view.getSelectedIndexes()[0];
16351         
16352         var r = this.store.getAt(index);
16353         
16354         if(this.tickable){
16355             
16356             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16357                 return;
16358             }
16359             
16360             var rm = false;
16361             var _this = this;
16362             
16363             Roo.each(this.tickItems, function(v,k){
16364                 
16365                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16366                     Roo.log(v);
16367                     _this.tickItems.splice(k, 1);
16368                     
16369                     if(typeof(e) == 'undefined' && view == false){
16370                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16371                     }
16372                     
16373                     rm = true;
16374                     return;
16375                 }
16376             });
16377             
16378             if(rm){
16379                 return;
16380             }
16381             
16382             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16383                 this.tickItems.push(r.data);
16384             }
16385             
16386             if(typeof(e) == 'undefined' && view == false){
16387                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16388             }
16389                     
16390             return;
16391         }
16392         
16393         if(r){
16394             this.onSelect(r, index);
16395         }
16396         if(doFocus !== false && !this.blockFocus){
16397             this.inputEl().focus();
16398         }
16399     },
16400
16401     // private
16402     restrictHeight : function(){
16403         //this.innerList.dom.style.height = '';
16404         //var inner = this.innerList.dom;
16405         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16406         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16407         //this.list.beginUpdate();
16408         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16409         this.list.alignTo(this.inputEl(), this.listAlign);
16410         this.list.alignTo(this.inputEl(), this.listAlign);
16411         //this.list.endUpdate();
16412     },
16413
16414     // private
16415     onEmptyResults : function(){
16416         
16417         if(this.tickable && this.editable){
16418             this.hasFocus = false;
16419             this.restrictHeight();
16420             return;
16421         }
16422         
16423         this.collapse();
16424     },
16425
16426     /**
16427      * Returns true if the dropdown list is expanded, else false.
16428      */
16429     isExpanded : function(){
16430         return this.list.isVisible();
16431     },
16432
16433     /**
16434      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16435      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16436      * @param {String} value The data value of the item to select
16437      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16438      * selected item if it is not currently in view (defaults to true)
16439      * @return {Boolean} True if the value matched an item in the list, else false
16440      */
16441     selectByValue : function(v, scrollIntoView){
16442         if(v !== undefined && v !== null){
16443             var r = this.findRecord(this.valueField || this.displayField, v);
16444             if(r){
16445                 this.select(this.store.indexOf(r), scrollIntoView);
16446                 return true;
16447             }
16448         }
16449         return false;
16450     },
16451
16452     /**
16453      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16455      * @param {Number} index The zero-based index of the list item to select
16456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16457      * selected item if it is not currently in view (defaults to true)
16458      */
16459     select : function(index, scrollIntoView){
16460         this.selectedIndex = index;
16461         this.view.select(index);
16462         if(scrollIntoView !== false){
16463             var el = this.view.getNode(index);
16464             /*
16465              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16466              */
16467             if(el){
16468                 this.list.scrollChildIntoView(el, false);
16469             }
16470         }
16471     },
16472
16473     // private
16474     selectNext : function(){
16475         var ct = this.store.getCount();
16476         if(ct > 0){
16477             if(this.selectedIndex == -1){
16478                 this.select(0);
16479             }else if(this.selectedIndex < ct-1){
16480                 this.select(this.selectedIndex+1);
16481             }
16482         }
16483     },
16484
16485     // private
16486     selectPrev : function(){
16487         var ct = this.store.getCount();
16488         if(ct > 0){
16489             if(this.selectedIndex == -1){
16490                 this.select(0);
16491             }else if(this.selectedIndex != 0){
16492                 this.select(this.selectedIndex-1);
16493             }
16494         }
16495     },
16496
16497     // private
16498     onKeyUp : function(e){
16499         if(this.editable !== false && !e.isSpecialKey()){
16500             this.lastKey = e.getKey();
16501             this.dqTask.delay(this.queryDelay);
16502         }
16503     },
16504
16505     // private
16506     validateBlur : function(){
16507         return !this.list || !this.list.isVisible();   
16508     },
16509
16510     // private
16511     initQuery : function(){
16512         
16513         var v = this.getRawValue();
16514         
16515         if(this.tickable && this.editable){
16516             v = this.tickableInputEl().getValue();
16517         }
16518         
16519         this.doQuery(v);
16520     },
16521
16522     // private
16523     doForce : function(){
16524         if(this.inputEl().dom.value.length > 0){
16525             this.inputEl().dom.value =
16526                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16527              
16528         }
16529     },
16530
16531     /**
16532      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16533      * query allowing the query action to be canceled if needed.
16534      * @param {String} query The SQL query to execute
16535      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16536      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16537      * saved in the current store (defaults to false)
16538      */
16539     doQuery : function(q, forceAll){
16540         
16541         if(q === undefined || q === null){
16542             q = '';
16543         }
16544         var qe = {
16545             query: q,
16546             forceAll: forceAll,
16547             combo: this,
16548             cancel:false
16549         };
16550         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16551             return false;
16552         }
16553         q = qe.query;
16554         
16555         forceAll = qe.forceAll;
16556         if(forceAll === true || (q.length >= this.minChars)){
16557             
16558             this.hasQuery = true;
16559             
16560             if(this.lastQuery != q || this.alwaysQuery){
16561                 this.lastQuery = q;
16562                 if(this.mode == 'local'){
16563                     this.selectedIndex = -1;
16564                     if(forceAll){
16565                         this.store.clearFilter();
16566                     }else{
16567                         
16568                         if(this.specialFilter){
16569                             this.fireEvent('specialfilter', this);
16570                             this.onLoad();
16571                             return;
16572                         }
16573                         
16574                         this.store.filter(this.displayField, q);
16575                     }
16576                     
16577                     this.store.fireEvent("datachanged", this.store);
16578                     
16579                     this.onLoad();
16580                     
16581                     
16582                 }else{
16583                     
16584                     this.store.baseParams[this.queryParam] = q;
16585                     
16586                     var options = {params : this.getParams(q)};
16587                     
16588                     if(this.loadNext){
16589                         options.add = true;
16590                         options.params.start = this.page * this.pageSize;
16591                     }
16592                     
16593                     this.store.load(options);
16594                     
16595                     /*
16596                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16597                      *  we should expand the list on onLoad
16598                      *  so command out it
16599                      */
16600 //                    this.expand();
16601                 }
16602             }else{
16603                 this.selectedIndex = -1;
16604                 this.onLoad();   
16605             }
16606         }
16607         
16608         this.loadNext = false;
16609     },
16610     
16611     // private
16612     getParams : function(q){
16613         var p = {};
16614         //p[this.queryParam] = q;
16615         
16616         if(this.pageSize){
16617             p.start = 0;
16618             p.limit = this.pageSize;
16619         }
16620         return p;
16621     },
16622
16623     /**
16624      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16625      */
16626     collapse : function(){
16627         if(!this.isExpanded()){
16628             return;
16629         }
16630         
16631         this.list.hide();
16632         
16633         this.hasFocus = false;
16634         
16635         if(this.tickable){
16636             this.okBtn.hide();
16637             this.cancelBtn.hide();
16638             this.trigger.show();
16639             
16640             if(this.editable){
16641                 this.tickableInputEl().dom.value = '';
16642                 this.tickableInputEl().blur();
16643             }
16644             
16645         }
16646         
16647         Roo.get(document).un('mousedown', this.collapseIf, this);
16648         Roo.get(document).un('mousewheel', this.collapseIf, this);
16649         if (!this.editable) {
16650             Roo.get(document).un('keydown', this.listKeyPress, this);
16651         }
16652         this.fireEvent('collapse', this);
16653         
16654         this.validate();
16655     },
16656
16657     // private
16658     collapseIf : function(e){
16659         var in_combo  = e.within(this.el);
16660         var in_list =  e.within(this.list);
16661         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16662         
16663         if (in_combo || in_list || is_list) {
16664             //e.stopPropagation();
16665             return;
16666         }
16667         
16668         if(this.tickable){
16669             this.onTickableFooterButtonClick(e, false, false);
16670         }
16671
16672         this.collapse();
16673         
16674     },
16675
16676     /**
16677      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16678      */
16679     expand : function(){
16680        
16681         if(this.isExpanded() || !this.hasFocus){
16682             return;
16683         }
16684         
16685         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16686         this.list.setWidth(lw);
16687         
16688         Roo.log('expand');
16689         
16690         this.list.show();
16691         
16692         this.restrictHeight();
16693         
16694         if(this.tickable){
16695             
16696             this.tickItems = Roo.apply([], this.item);
16697             
16698             this.okBtn.show();
16699             this.cancelBtn.show();
16700             this.trigger.hide();
16701             
16702             if(this.editable){
16703                 this.tickableInputEl().focus();
16704             }
16705             
16706         }
16707         
16708         Roo.get(document).on('mousedown', this.collapseIf, this);
16709         Roo.get(document).on('mousewheel', this.collapseIf, this);
16710         if (!this.editable) {
16711             Roo.get(document).on('keydown', this.listKeyPress, this);
16712         }
16713         
16714         this.fireEvent('expand', this);
16715     },
16716
16717     // private
16718     // Implements the default empty TriggerField.onTriggerClick function
16719     onTriggerClick : function(e)
16720     {
16721         Roo.log('trigger click');
16722         
16723         if(this.disabled || !this.triggerList){
16724             return;
16725         }
16726         
16727         this.page = 0;
16728         this.loadNext = false;
16729         
16730         if(this.isExpanded()){
16731             this.collapse();
16732             if (!this.blockFocus) {
16733                 this.inputEl().focus();
16734             }
16735             
16736         }else {
16737             this.hasFocus = true;
16738             if(this.triggerAction == 'all') {
16739                 this.doQuery(this.allQuery, true);
16740             } else {
16741                 this.doQuery(this.getRawValue());
16742             }
16743             if (!this.blockFocus) {
16744                 this.inputEl().focus();
16745             }
16746         }
16747     },
16748     
16749     onTickableTriggerClick : function(e)
16750     {
16751         if(this.disabled){
16752             return;
16753         }
16754         
16755         this.page = 0;
16756         this.loadNext = false;
16757         this.hasFocus = true;
16758         
16759         if(this.triggerAction == 'all') {
16760             this.doQuery(this.allQuery, true);
16761         } else {
16762             this.doQuery(this.getRawValue());
16763         }
16764     },
16765     
16766     onSearchFieldClick : function(e)
16767     {
16768         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16769             this.onTickableFooterButtonClick(e, false, false);
16770             return;
16771         }
16772         
16773         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16774             return;
16775         }
16776         
16777         this.page = 0;
16778         this.loadNext = false;
16779         this.hasFocus = true;
16780         
16781         if(this.triggerAction == 'all') {
16782             this.doQuery(this.allQuery, true);
16783         } else {
16784             this.doQuery(this.getRawValue());
16785         }
16786     },
16787     
16788     listKeyPress : function(e)
16789     {
16790         //Roo.log('listkeypress');
16791         // scroll to first matching element based on key pres..
16792         if (e.isSpecialKey()) {
16793             return false;
16794         }
16795         var k = String.fromCharCode(e.getKey()).toUpperCase();
16796         //Roo.log(k);
16797         var match  = false;
16798         var csel = this.view.getSelectedNodes();
16799         var cselitem = false;
16800         if (csel.length) {
16801             var ix = this.view.indexOf(csel[0]);
16802             cselitem  = this.store.getAt(ix);
16803             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16804                 cselitem = false;
16805             }
16806             
16807         }
16808         
16809         this.store.each(function(v) { 
16810             if (cselitem) {
16811                 // start at existing selection.
16812                 if (cselitem.id == v.id) {
16813                     cselitem = false;
16814                 }
16815                 return true;
16816             }
16817                 
16818             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16819                 match = this.store.indexOf(v);
16820                 return false;
16821             }
16822             return true;
16823         }, this);
16824         
16825         if (match === false) {
16826             return true; // no more action?
16827         }
16828         // scroll to?
16829         this.view.select(match);
16830         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16831         sn.scrollIntoView(sn.dom.parentNode, false);
16832     },
16833     
16834     onViewScroll : function(e, t){
16835         
16836         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){
16837             return;
16838         }
16839         
16840         this.hasQuery = true;
16841         
16842         this.loading = this.list.select('.loading', true).first();
16843         
16844         if(this.loading === null){
16845             this.list.createChild({
16846                 tag: 'div',
16847                 cls: 'loading roo-select2-more-results roo-select2-active',
16848                 html: 'Loading more results...'
16849             });
16850             
16851             this.loading = this.list.select('.loading', true).first();
16852             
16853             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16854             
16855             this.loading.hide();
16856         }
16857         
16858         this.loading.show();
16859         
16860         var _combo = this;
16861         
16862         this.page++;
16863         this.loadNext = true;
16864         
16865         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16866         
16867         return;
16868     },
16869     
16870     addItem : function(o)
16871     {   
16872         var dv = ''; // display value
16873         
16874         if (this.displayField) {
16875             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16876         } else {
16877             // this is an error condition!!!
16878             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16879         }
16880         
16881         if(!dv.length){
16882             return;
16883         }
16884         
16885         var choice = this.choices.createChild({
16886             tag: 'li',
16887             cls: 'roo-select2-search-choice',
16888             cn: [
16889                 {
16890                     tag: 'div',
16891                     html: dv
16892                 },
16893                 {
16894                     tag: 'a',
16895                     href: '#',
16896                     cls: 'roo-select2-search-choice-close fa fa-times',
16897                     tabindex: '-1'
16898                 }
16899             ]
16900             
16901         }, this.searchField);
16902         
16903         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16904         
16905         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16906         
16907         this.item.push(o);
16908         
16909         this.lastData = o;
16910         
16911         this.syncValue();
16912         
16913         this.inputEl().dom.value = '';
16914         
16915         this.validate();
16916     },
16917     
16918     onRemoveItem : function(e, _self, o)
16919     {
16920         e.preventDefault();
16921         
16922         this.lastItem = Roo.apply([], this.item);
16923         
16924         var index = this.item.indexOf(o.data) * 1;
16925         
16926         if( index < 0){
16927             Roo.log('not this item?!');
16928             return;
16929         }
16930         
16931         this.item.splice(index, 1);
16932         o.item.remove();
16933         
16934         this.syncValue();
16935         
16936         this.fireEvent('remove', this, e);
16937         
16938         this.validate();
16939         
16940     },
16941     
16942     syncValue : function()
16943     {
16944         if(!this.item.length){
16945             this.clearValue();
16946             return;
16947         }
16948             
16949         var value = [];
16950         var _this = this;
16951         Roo.each(this.item, function(i){
16952             if(_this.valueField){
16953                 value.push(i[_this.valueField]);
16954                 return;
16955             }
16956
16957             value.push(i);
16958         });
16959
16960         this.value = value.join(',');
16961
16962         if(this.hiddenField){
16963             this.hiddenField.dom.value = this.value;
16964         }
16965         
16966         this.store.fireEvent("datachanged", this.store);
16967         
16968         this.validate();
16969     },
16970     
16971     clearItem : function()
16972     {
16973         if(!this.multiple){
16974             return;
16975         }
16976         
16977         this.item = [];
16978         
16979         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16980            c.remove();
16981         });
16982         
16983         this.syncValue();
16984         
16985         this.validate();
16986         
16987         if(this.tickable && !Roo.isTouch){
16988             this.view.refresh();
16989         }
16990     },
16991     
16992     inputEl: function ()
16993     {
16994         if(Roo.isIOS && this.useNativeIOS){
16995             return this.el.select('select.roo-ios-select', true).first();
16996         }
16997         
16998         if(Roo.isTouch && this.mobileTouchView){
16999             return this.el.select('input.form-control',true).first();
17000         }
17001         
17002         if(this.tickable){
17003             return this.searchField;
17004         }
17005         
17006         return this.el.select('input.form-control',true).first();
17007     },
17008     
17009     onTickableFooterButtonClick : function(e, btn, el)
17010     {
17011         e.preventDefault();
17012         
17013         this.lastItem = Roo.apply([], this.item);
17014         
17015         if(btn && btn.name == 'cancel'){
17016             this.tickItems = Roo.apply([], this.item);
17017             this.collapse();
17018             return;
17019         }
17020         
17021         this.clearItem();
17022         
17023         var _this = this;
17024         
17025         Roo.each(this.tickItems, function(o){
17026             _this.addItem(o);
17027         });
17028         
17029         this.collapse();
17030         
17031     },
17032     
17033     validate : function()
17034     {
17035         if(this.getVisibilityEl().hasClass('hidden')){
17036             return true;
17037         }
17038         
17039         var v = this.getRawValue();
17040         
17041         if(this.multiple){
17042             v = this.getValue();
17043         }
17044         
17045         if(this.disabled || this.allowBlank || v.length){
17046             this.markValid();
17047             return true;
17048         }
17049         
17050         this.markInvalid();
17051         return false;
17052     },
17053     
17054     tickableInputEl : function()
17055     {
17056         if(!this.tickable || !this.editable){
17057             return this.inputEl();
17058         }
17059         
17060         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17061     },
17062     
17063     
17064     getAutoCreateTouchView : function()
17065     {
17066         var id = Roo.id();
17067         
17068         var cfg = {
17069             cls: 'form-group' //input-group
17070         };
17071         
17072         var input =  {
17073             tag: 'input',
17074             id : id,
17075             type : this.inputType,
17076             cls : 'form-control x-combo-noedit',
17077             autocomplete: 'new-password',
17078             placeholder : this.placeholder || '',
17079             readonly : true
17080         };
17081         
17082         if (this.name) {
17083             input.name = this.name;
17084         }
17085         
17086         if (this.size) {
17087             input.cls += ' input-' + this.size;
17088         }
17089         
17090         if (this.disabled) {
17091             input.disabled = true;
17092         }
17093         
17094         var inputblock = {
17095             cls : 'roo-combobox-wrap',
17096             cn : [
17097                 input
17098             ]
17099         };
17100         
17101         if(this.before){
17102             inputblock.cls += ' input-group';
17103             
17104             inputblock.cn.unshift({
17105                 tag :'span',
17106                 cls : 'input-group-addon input-group-prepend input-group-text',
17107                 html : this.before
17108             });
17109         }
17110         
17111         if(this.removable && !this.multiple){
17112             inputblock.cls += ' roo-removable';
17113             
17114             inputblock.cn.push({
17115                 tag: 'button',
17116                 html : 'x',
17117                 cls : 'roo-combo-removable-btn close'
17118             });
17119         }
17120
17121         if(this.hasFeedback && !this.allowBlank){
17122             
17123             inputblock.cls += ' has-feedback';
17124             
17125             inputblock.cn.push({
17126                 tag: 'span',
17127                 cls: 'glyphicon form-control-feedback'
17128             });
17129             
17130         }
17131         
17132         if (this.after) {
17133             
17134             inputblock.cls += (this.before) ? '' : ' input-group';
17135             
17136             inputblock.cn.push({
17137                 tag :'span',
17138                 cls : 'input-group-addon input-group-append input-group-text',
17139                 html : this.after
17140             });
17141         }
17142
17143         
17144         var ibwrap = inputblock;
17145         
17146         if(this.multiple){
17147             ibwrap = {
17148                 tag: 'ul',
17149                 cls: 'roo-select2-choices',
17150                 cn:[
17151                     {
17152                         tag: 'li',
17153                         cls: 'roo-select2-search-field',
17154                         cn: [
17155
17156                             inputblock
17157                         ]
17158                     }
17159                 ]
17160             };
17161         
17162             
17163         }
17164         
17165         var combobox = {
17166             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17167             cn: [
17168                 {
17169                     tag: 'input',
17170                     type : 'hidden',
17171                     cls: 'form-hidden-field'
17172                 },
17173                 ibwrap
17174             ]
17175         };
17176         
17177         if(!this.multiple && this.showToggleBtn){
17178             
17179             var caret = {
17180                 cls: 'caret'
17181             };
17182             
17183             if (this.caret != false) {
17184                 caret = {
17185                      tag: 'i',
17186                      cls: 'fa fa-' + this.caret
17187                 };
17188                 
17189             }
17190             
17191             combobox.cn.push({
17192                 tag :'span',
17193                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17194                 cn : [
17195                     Roo.bootstrap.version == 3 ? caret : '',
17196                     {
17197                         tag: 'span',
17198                         cls: 'combobox-clear',
17199                         cn  : [
17200                             {
17201                                 tag : 'i',
17202                                 cls: 'icon-remove'
17203                             }
17204                         ]
17205                     }
17206                 ]
17207
17208             })
17209         }
17210         
17211         if(this.multiple){
17212             combobox.cls += ' roo-select2-container-multi';
17213         }
17214         
17215         var align = this.labelAlign || this.parentLabelAlign();
17216         
17217         if (align ==='left' && this.fieldLabel.length) {
17218
17219             cfg.cn = [
17220                 {
17221                    tag : 'i',
17222                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17223                    tooltip : 'This field is required'
17224                 },
17225                 {
17226                     tag: 'label',
17227                     cls : 'control-label col-form-label',
17228                     html : this.fieldLabel
17229
17230                 },
17231                 {
17232                     cls : 'roo-combobox-wrap ', 
17233                     cn: [
17234                         combobox
17235                     ]
17236                 }
17237             ];
17238             
17239             var labelCfg = cfg.cn[1];
17240             var contentCfg = cfg.cn[2];
17241             
17242
17243             if(this.indicatorpos == 'right'){
17244                 cfg.cn = [
17245                     {
17246                         tag: 'label',
17247                         'for' :  id,
17248                         cls : 'control-label col-form-label',
17249                         cn : [
17250                             {
17251                                 tag : 'span',
17252                                 html : this.fieldLabel
17253                             },
17254                             {
17255                                 tag : 'i',
17256                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17257                                 tooltip : 'This field is required'
17258                             }
17259                         ]
17260                     },
17261                     {
17262                         cls : "roo-combobox-wrap ",
17263                         cn: [
17264                             combobox
17265                         ]
17266                     }
17267
17268                 ];
17269                 
17270                 labelCfg = cfg.cn[0];
17271                 contentCfg = cfg.cn[1];
17272             }
17273             
17274            
17275             
17276             if(this.labelWidth > 12){
17277                 labelCfg.style = "width: " + this.labelWidth + 'px';
17278             }
17279            
17280             if(this.labelWidth < 13 && this.labelmd == 0){
17281                 this.labelmd = this.labelWidth;
17282             }
17283             
17284             if(this.labellg > 0){
17285                 labelCfg.cls += ' col-lg-' + this.labellg;
17286                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17287             }
17288             
17289             if(this.labelmd > 0){
17290                 labelCfg.cls += ' col-md-' + this.labelmd;
17291                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17292             }
17293             
17294             if(this.labelsm > 0){
17295                 labelCfg.cls += ' col-sm-' + this.labelsm;
17296                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17297             }
17298             
17299             if(this.labelxs > 0){
17300                 labelCfg.cls += ' col-xs-' + this.labelxs;
17301                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17302             }
17303                 
17304                 
17305         } else if ( this.fieldLabel.length) {
17306             cfg.cn = [
17307                 {
17308                    tag : 'i',
17309                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17310                    tooltip : 'This field is required'
17311                 },
17312                 {
17313                     tag: 'label',
17314                     cls : 'control-label',
17315                     html : this.fieldLabel
17316
17317                 },
17318                 {
17319                     cls : '', 
17320                     cn: [
17321                         combobox
17322                     ]
17323                 }
17324             ];
17325             
17326             if(this.indicatorpos == 'right'){
17327                 cfg.cn = [
17328                     {
17329                         tag: 'label',
17330                         cls : 'control-label',
17331                         html : this.fieldLabel,
17332                         cn : [
17333                             {
17334                                tag : 'i',
17335                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17336                                tooltip : 'This field is required'
17337                             }
17338                         ]
17339                     },
17340                     {
17341                         cls : '', 
17342                         cn: [
17343                             combobox
17344                         ]
17345                     }
17346                 ];
17347             }
17348         } else {
17349             cfg.cn = combobox;    
17350         }
17351         
17352         
17353         var settings = this;
17354         
17355         ['xs','sm','md','lg'].map(function(size){
17356             if (settings[size]) {
17357                 cfg.cls += ' col-' + size + '-' + settings[size];
17358             }
17359         });
17360         
17361         return cfg;
17362     },
17363     
17364     initTouchView : function()
17365     {
17366         this.renderTouchView();
17367         
17368         this.touchViewEl.on('scroll', function(){
17369             this.el.dom.scrollTop = 0;
17370         }, this);
17371         
17372         this.originalValue = this.getValue();
17373         
17374         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17375         
17376         this.inputEl().on("click", this.showTouchView, this);
17377         if (this.triggerEl) {
17378             this.triggerEl.on("click", this.showTouchView, this);
17379         }
17380         
17381         
17382         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17383         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17384         
17385         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17386         
17387         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17388         this.store.on('load', this.onTouchViewLoad, this);
17389         this.store.on('loadexception', this.onTouchViewLoadException, this);
17390         
17391         if(this.hiddenName){
17392             
17393             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17394             
17395             this.hiddenField.dom.value =
17396                 this.hiddenValue !== undefined ? this.hiddenValue :
17397                 this.value !== undefined ? this.value : '';
17398         
17399             this.el.dom.removeAttribute('name');
17400             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17401         }
17402         
17403         if(this.multiple){
17404             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17405             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17406         }
17407         
17408         if(this.removable && !this.multiple){
17409             var close = this.closeTriggerEl();
17410             if(close){
17411                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17412                 close.on('click', this.removeBtnClick, this, close);
17413             }
17414         }
17415         /*
17416          * fix the bug in Safari iOS8
17417          */
17418         this.inputEl().on("focus", function(e){
17419             document.activeElement.blur();
17420         }, this);
17421         
17422         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17423         
17424         return;
17425         
17426         
17427     },
17428     
17429     renderTouchView : function()
17430     {
17431         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17432         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17433         
17434         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17435         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17438         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         this.touchViewBodyEl.setStyle('overflow', 'auto');
17440         
17441         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17442         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17443         
17444         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17445         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447     },
17448     
17449     showTouchView : function()
17450     {
17451         if(this.disabled){
17452             return;
17453         }
17454         
17455         this.touchViewHeaderEl.hide();
17456
17457         if(this.modalTitle.length){
17458             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17459             this.touchViewHeaderEl.show();
17460         }
17461
17462         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17463         this.touchViewEl.show();
17464
17465         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17466         
17467         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17468         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17469
17470         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17471
17472         if(this.modalTitle.length){
17473             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17474         }
17475         
17476         this.touchViewBodyEl.setHeight(bodyHeight);
17477
17478         if(this.animate){
17479             var _this = this;
17480             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17481         }else{
17482             this.touchViewEl.addClass(['in','show']);
17483         }
17484         
17485         if(this._touchViewMask){
17486             Roo.get(document.body).addClass("x-body-masked");
17487             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17488             this._touchViewMask.setStyle('z-index', 10000);
17489             this._touchViewMask.addClass('show');
17490         }
17491         
17492         this.doTouchViewQuery();
17493         
17494     },
17495     
17496     hideTouchView : function()
17497     {
17498         this.touchViewEl.removeClass(['in','show']);
17499
17500         if(this.animate){
17501             var _this = this;
17502             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17503         }else{
17504             this.touchViewEl.setStyle('display', 'none');
17505         }
17506         
17507         if(this._touchViewMask){
17508             this._touchViewMask.removeClass('show');
17509             Roo.get(document.body).removeClass("x-body-masked");
17510         }
17511     },
17512     
17513     setTouchViewValue : function()
17514     {
17515         if(this.multiple){
17516             this.clearItem();
17517         
17518             var _this = this;
17519
17520             Roo.each(this.tickItems, function(o){
17521                 this.addItem(o);
17522             }, this);
17523         }
17524         
17525         this.hideTouchView();
17526     },
17527     
17528     doTouchViewQuery : function()
17529     {
17530         var qe = {
17531             query: '',
17532             forceAll: true,
17533             combo: this,
17534             cancel:false
17535         };
17536         
17537         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17538             return false;
17539         }
17540         
17541         if(!this.alwaysQuery || this.mode == 'local'){
17542             this.onTouchViewLoad();
17543             return;
17544         }
17545         
17546         this.store.load();
17547     },
17548     
17549     onTouchViewBeforeLoad : function(combo,opts)
17550     {
17551         return;
17552     },
17553
17554     // private
17555     onTouchViewLoad : function()
17556     {
17557         if(this.store.getCount() < 1){
17558             this.onTouchViewEmptyResults();
17559             return;
17560         }
17561         
17562         this.clearTouchView();
17563         
17564         var rawValue = this.getRawValue();
17565         
17566         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17567         
17568         this.tickItems = [];
17569         
17570         this.store.data.each(function(d, rowIndex){
17571             var row = this.touchViewListGroup.createChild(template);
17572             
17573             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17574                 row.addClass(d.data.cls);
17575             }
17576             
17577             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17578                 var cfg = {
17579                     data : d.data,
17580                     html : d.data[this.displayField]
17581                 };
17582                 
17583                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17584                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17585                 }
17586             }
17587             row.removeClass('selected');
17588             if(!this.multiple && this.valueField &&
17589                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17590             {
17591                 // radio buttons..
17592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17593                 row.addClass('selected');
17594             }
17595             
17596             if(this.multiple && this.valueField &&
17597                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17598             {
17599                 
17600                 // checkboxes...
17601                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17602                 this.tickItems.push(d.data);
17603             }
17604             
17605             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17606             
17607         }, this);
17608         
17609         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17610         
17611         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17612
17613         if(this.modalTitle.length){
17614             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17615         }
17616
17617         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17618         
17619         if(this.mobile_restrict_height && listHeight < bodyHeight){
17620             this.touchViewBodyEl.setHeight(listHeight);
17621         }
17622         
17623         var _this = this;
17624         
17625         if(firstChecked && listHeight > bodyHeight){
17626             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17627         }
17628         
17629     },
17630     
17631     onTouchViewLoadException : function()
17632     {
17633         this.hideTouchView();
17634     },
17635     
17636     onTouchViewEmptyResults : function()
17637     {
17638         this.clearTouchView();
17639         
17640         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17641         
17642         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17643         
17644     },
17645     
17646     clearTouchView : function()
17647     {
17648         this.touchViewListGroup.dom.innerHTML = '';
17649     },
17650     
17651     onTouchViewClick : function(e, el, o)
17652     {
17653         e.preventDefault();
17654         
17655         var row = o.row;
17656         var rowIndex = o.rowIndex;
17657         
17658         var r = this.store.getAt(rowIndex);
17659         
17660         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17661             
17662             if(!this.multiple){
17663                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17664                     c.dom.removeAttribute('checked');
17665                 }, this);
17666
17667                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17668
17669                 this.setFromData(r.data);
17670
17671                 var close = this.closeTriggerEl();
17672
17673                 if(close){
17674                     close.show();
17675                 }
17676
17677                 this.hideTouchView();
17678
17679                 this.fireEvent('select', this, r, rowIndex);
17680
17681                 return;
17682             }
17683
17684             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17685                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17686                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17687                 return;
17688             }
17689
17690             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691             this.addItem(r.data);
17692             this.tickItems.push(r.data);
17693         }
17694     },
17695     
17696     getAutoCreateNativeIOS : function()
17697     {
17698         var cfg = {
17699             cls: 'form-group' //input-group,
17700         };
17701         
17702         var combobox =  {
17703             tag: 'select',
17704             cls : 'roo-ios-select'
17705         };
17706         
17707         if (this.name) {
17708             combobox.name = this.name;
17709         }
17710         
17711         if (this.disabled) {
17712             combobox.disabled = true;
17713         }
17714         
17715         var settings = this;
17716         
17717         ['xs','sm','md','lg'].map(function(size){
17718             if (settings[size]) {
17719                 cfg.cls += ' col-' + size + '-' + settings[size];
17720             }
17721         });
17722         
17723         cfg.cn = combobox;
17724         
17725         return cfg;
17726         
17727     },
17728     
17729     initIOSView : function()
17730     {
17731         this.store.on('load', this.onIOSViewLoad, this);
17732         
17733         return;
17734     },
17735     
17736     onIOSViewLoad : function()
17737     {
17738         if(this.store.getCount() < 1){
17739             return;
17740         }
17741         
17742         this.clearIOSView();
17743         
17744         if(this.allowBlank) {
17745             
17746             var default_text = '-- SELECT --';
17747             
17748             if(this.placeholder.length){
17749                 default_text = this.placeholder;
17750             }
17751             
17752             if(this.emptyTitle.length){
17753                 default_text += ' - ' + this.emptyTitle + ' -';
17754             }
17755             
17756             var opt = this.inputEl().createChild({
17757                 tag: 'option',
17758                 value : 0,
17759                 html : default_text
17760             });
17761             
17762             var o = {};
17763             o[this.valueField] = 0;
17764             o[this.displayField] = default_text;
17765             
17766             this.ios_options.push({
17767                 data : o,
17768                 el : opt
17769             });
17770             
17771         }
17772         
17773         this.store.data.each(function(d, rowIndex){
17774             
17775             var html = '';
17776             
17777             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17778                 html = d.data[this.displayField];
17779             }
17780             
17781             var value = '';
17782             
17783             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17784                 value = d.data[this.valueField];
17785             }
17786             
17787             var option = {
17788                 tag: 'option',
17789                 value : value,
17790                 html : html
17791             };
17792             
17793             if(this.value == d.data[this.valueField]){
17794                 option['selected'] = true;
17795             }
17796             
17797             var opt = this.inputEl().createChild(option);
17798             
17799             this.ios_options.push({
17800                 data : d.data,
17801                 el : opt
17802             });
17803             
17804         }, this);
17805         
17806         this.inputEl().on('change', function(){
17807            this.fireEvent('select', this);
17808         }, this);
17809         
17810     },
17811     
17812     clearIOSView: function()
17813     {
17814         this.inputEl().dom.innerHTML = '';
17815         
17816         this.ios_options = [];
17817     },
17818     
17819     setIOSValue: function(v)
17820     {
17821         this.value = v;
17822         
17823         if(!this.ios_options){
17824             return;
17825         }
17826         
17827         Roo.each(this.ios_options, function(opts){
17828            
17829            opts.el.dom.removeAttribute('selected');
17830            
17831            if(opts.data[this.valueField] != v){
17832                return;
17833            }
17834            
17835            opts.el.dom.setAttribute('selected', true);
17836            
17837         }, this);
17838     }
17839
17840     /** 
17841     * @cfg {Boolean} grow 
17842     * @hide 
17843     */
17844     /** 
17845     * @cfg {Number} growMin 
17846     * @hide 
17847     */
17848     /** 
17849     * @cfg {Number} growMax 
17850     * @hide 
17851     */
17852     /**
17853      * @hide
17854      * @method autoSize
17855      */
17856 });
17857
17858 Roo.apply(Roo.bootstrap.ComboBox,  {
17859     
17860     header : {
17861         tag: 'div',
17862         cls: 'modal-header',
17863         cn: [
17864             {
17865                 tag: 'h4',
17866                 cls: 'modal-title'
17867             }
17868         ]
17869     },
17870     
17871     body : {
17872         tag: 'div',
17873         cls: 'modal-body',
17874         cn: [
17875             {
17876                 tag: 'ul',
17877                 cls: 'list-group'
17878             }
17879         ]
17880     },
17881     
17882     listItemRadio : {
17883         tag: 'li',
17884         cls: 'list-group-item',
17885         cn: [
17886             {
17887                 tag: 'span',
17888                 cls: 'roo-combobox-list-group-item-value'
17889             },
17890             {
17891                 tag: 'div',
17892                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17893                 cn: [
17894                     {
17895                         tag: 'input',
17896                         type: 'radio'
17897                     },
17898                     {
17899                         tag: 'label'
17900                     }
17901                 ]
17902             }
17903         ]
17904     },
17905     
17906     listItemCheckbox : {
17907         tag: 'li',
17908         cls: 'list-group-item',
17909         cn: [
17910             {
17911                 tag: 'span',
17912                 cls: 'roo-combobox-list-group-item-value'
17913             },
17914             {
17915                 tag: 'div',
17916                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17917                 cn: [
17918                     {
17919                         tag: 'input',
17920                         type: 'checkbox'
17921                     },
17922                     {
17923                         tag: 'label'
17924                     }
17925                 ]
17926             }
17927         ]
17928     },
17929     
17930     emptyResult : {
17931         tag: 'div',
17932         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17933     },
17934     
17935     footer : {
17936         tag: 'div',
17937         cls: 'modal-footer',
17938         cn: [
17939             {
17940                 tag: 'div',
17941                 cls: 'row',
17942                 cn: [
17943                     {
17944                         tag: 'div',
17945                         cls: 'col-xs-6 text-left',
17946                         cn: {
17947                             tag: 'button',
17948                             cls: 'btn btn-danger roo-touch-view-cancel',
17949                             html: 'Cancel'
17950                         }
17951                     },
17952                     {
17953                         tag: 'div',
17954                         cls: 'col-xs-6 text-right',
17955                         cn: {
17956                             tag: 'button',
17957                             cls: 'btn btn-success roo-touch-view-ok',
17958                             html: 'OK'
17959                         }
17960                     }
17961                 ]
17962             }
17963         ]
17964         
17965     }
17966 });
17967
17968 Roo.apply(Roo.bootstrap.ComboBox,  {
17969     
17970     touchViewTemplate : {
17971         tag: 'div',
17972         cls: 'modal fade roo-combobox-touch-view',
17973         cn: [
17974             {
17975                 tag: 'div',
17976                 cls: 'modal-dialog',
17977                 style : 'position:fixed', // we have to fix position....
17978                 cn: [
17979                     {
17980                         tag: 'div',
17981                         cls: 'modal-content',
17982                         cn: [
17983                             Roo.bootstrap.ComboBox.header,
17984                             Roo.bootstrap.ComboBox.body,
17985                             Roo.bootstrap.ComboBox.footer
17986                         ]
17987                     }
17988                 ]
17989             }
17990         ]
17991     }
17992 });/*
17993  * Based on:
17994  * Ext JS Library 1.1.1
17995  * Copyright(c) 2006-2007, Ext JS, LLC.
17996  *
17997  * Originally Released Under LGPL - original licence link has changed is not relivant.
17998  *
17999  * Fork - LGPL
18000  * <script type="text/javascript">
18001  */
18002
18003 /**
18004  * @class Roo.View
18005  * @extends Roo.util.Observable
18006  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18007  * This class also supports single and multi selection modes. <br>
18008  * Create a data model bound view:
18009  <pre><code>
18010  var store = new Roo.data.Store(...);
18011
18012  var view = new Roo.View({
18013     el : "my-element",
18014     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18015  
18016     singleSelect: true,
18017     selectedClass: "ydataview-selected",
18018     store: store
18019  });
18020
18021  // listen for node click?
18022  view.on("click", function(vw, index, node, e){
18023  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18024  });
18025
18026  // load XML data
18027  dataModel.load("foobar.xml");
18028  </code></pre>
18029  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18030  * <br><br>
18031  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18032  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18033  * 
18034  * Note: old style constructor is still suported (container, template, config)
18035  * 
18036  * @constructor
18037  * Create a new View
18038  * @param {Object} config The config object
18039  * 
18040  */
18041 Roo.View = function(config, depreciated_tpl, depreciated_config){
18042     
18043     this.parent = false;
18044     
18045     if (typeof(depreciated_tpl) == 'undefined') {
18046         // new way.. - universal constructor.
18047         Roo.apply(this, config);
18048         this.el  = Roo.get(this.el);
18049     } else {
18050         // old format..
18051         this.el  = Roo.get(config);
18052         this.tpl = depreciated_tpl;
18053         Roo.apply(this, depreciated_config);
18054     }
18055     this.wrapEl  = this.el.wrap().wrap();
18056     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18057     
18058     
18059     if(typeof(this.tpl) == "string"){
18060         this.tpl = new Roo.Template(this.tpl);
18061     } else {
18062         // support xtype ctors..
18063         this.tpl = new Roo.factory(this.tpl, Roo);
18064     }
18065     
18066     
18067     this.tpl.compile();
18068     
18069     /** @private */
18070     this.addEvents({
18071         /**
18072          * @event beforeclick
18073          * Fires before a click is processed. Returns false to cancel the default action.
18074          * @param {Roo.View} this
18075          * @param {Number} index The index of the target node
18076          * @param {HTMLElement} node The target node
18077          * @param {Roo.EventObject} e The raw event object
18078          */
18079             "beforeclick" : true,
18080         /**
18081          * @event click
18082          * Fires when a template node is clicked.
18083          * @param {Roo.View} this
18084          * @param {Number} index The index of the target node
18085          * @param {HTMLElement} node The target node
18086          * @param {Roo.EventObject} e The raw event object
18087          */
18088             "click" : true,
18089         /**
18090          * @event dblclick
18091          * Fires when a template node is double clicked.
18092          * @param {Roo.View} this
18093          * @param {Number} index The index of the target node
18094          * @param {HTMLElement} node The target node
18095          * @param {Roo.EventObject} e The raw event object
18096          */
18097             "dblclick" : true,
18098         /**
18099          * @event contextmenu
18100          * Fires when a template node is right clicked.
18101          * @param {Roo.View} this
18102          * @param {Number} index The index of the target node
18103          * @param {HTMLElement} node The target node
18104          * @param {Roo.EventObject} e The raw event object
18105          */
18106             "contextmenu" : true,
18107         /**
18108          * @event selectionchange
18109          * Fires when the selected nodes change.
18110          * @param {Roo.View} this
18111          * @param {Array} selections Array of the selected nodes
18112          */
18113             "selectionchange" : true,
18114     
18115         /**
18116          * @event beforeselect
18117          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18118          * @param {Roo.View} this
18119          * @param {HTMLElement} node The node to be selected
18120          * @param {Array} selections Array of currently selected nodes
18121          */
18122             "beforeselect" : true,
18123         /**
18124          * @event preparedata
18125          * Fires on every row to render, to allow you to change the data.
18126          * @param {Roo.View} this
18127          * @param {Object} data to be rendered (change this)
18128          */
18129           "preparedata" : true
18130           
18131           
18132         });
18133
18134
18135
18136     this.el.on({
18137         "click": this.onClick,
18138         "dblclick": this.onDblClick,
18139         "contextmenu": this.onContextMenu,
18140         scope:this
18141     });
18142
18143     this.selections = [];
18144     this.nodes = [];
18145     this.cmp = new Roo.CompositeElementLite([]);
18146     if(this.store){
18147         this.store = Roo.factory(this.store, Roo.data);
18148         this.setStore(this.store, true);
18149     }
18150     
18151     if ( this.footer && this.footer.xtype) {
18152            
18153          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18154         
18155         this.footer.dataSource = this.store;
18156         this.footer.container = fctr;
18157         this.footer = Roo.factory(this.footer, Roo);
18158         fctr.insertFirst(this.el);
18159         
18160         // this is a bit insane - as the paging toolbar seems to detach the el..
18161 //        dom.parentNode.parentNode.parentNode
18162          // they get detached?
18163     }
18164     
18165     
18166     Roo.View.superclass.constructor.call(this);
18167     
18168     
18169 };
18170
18171 Roo.extend(Roo.View, Roo.util.Observable, {
18172     
18173      /**
18174      * @cfg {Roo.data.Store} store Data store to load data from.
18175      */
18176     store : false,
18177     
18178     /**
18179      * @cfg {String|Roo.Element} el The container element.
18180      */
18181     el : '',
18182     
18183     /**
18184      * @cfg {String|Roo.Template} tpl The template used by this View 
18185      */
18186     tpl : false,
18187     /**
18188      * @cfg {String} dataName the named area of the template to use as the data area
18189      *                          Works with domtemplates roo-name="name"
18190      */
18191     dataName: false,
18192     /**
18193      * @cfg {String} selectedClass The css class to add to selected nodes
18194      */
18195     selectedClass : "x-view-selected",
18196      /**
18197      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18198      */
18199     emptyText : "",
18200     
18201     /**
18202      * @cfg {String} text to display on mask (default Loading)
18203      */
18204     mask : false,
18205     /**
18206      * @cfg {Boolean} multiSelect Allow multiple selection
18207      */
18208     multiSelect : false,
18209     /**
18210      * @cfg {Boolean} singleSelect Allow single selection
18211      */
18212     singleSelect:  false,
18213     
18214     /**
18215      * @cfg {Boolean} toggleSelect - selecting 
18216      */
18217     toggleSelect : false,
18218     
18219     /**
18220      * @cfg {Boolean} tickable - selecting 
18221      */
18222     tickable : false,
18223     
18224     /**
18225      * Returns the element this view is bound to.
18226      * @return {Roo.Element}
18227      */
18228     getEl : function(){
18229         return this.wrapEl;
18230     },
18231     
18232     
18233
18234     /**
18235      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18236      */
18237     refresh : function(){
18238         //Roo.log('refresh');
18239         var t = this.tpl;
18240         
18241         // if we are using something like 'domtemplate', then
18242         // the what gets used is:
18243         // t.applySubtemplate(NAME, data, wrapping data..)
18244         // the outer template then get' applied with
18245         //     the store 'extra data'
18246         // and the body get's added to the
18247         //      roo-name="data" node?
18248         //      <span class='roo-tpl-{name}'></span> ?????
18249         
18250         
18251         
18252         this.clearSelections();
18253         this.el.update("");
18254         var html = [];
18255         var records = this.store.getRange();
18256         if(records.length < 1) {
18257             
18258             // is this valid??  = should it render a template??
18259             
18260             this.el.update(this.emptyText);
18261             return;
18262         }
18263         var el = this.el;
18264         if (this.dataName) {
18265             this.el.update(t.apply(this.store.meta)); //????
18266             el = this.el.child('.roo-tpl-' + this.dataName);
18267         }
18268         
18269         for(var i = 0, len = records.length; i < len; i++){
18270             var data = this.prepareData(records[i].data, i, records[i]);
18271             this.fireEvent("preparedata", this, data, i, records[i]);
18272             
18273             var d = Roo.apply({}, data);
18274             
18275             if(this.tickable){
18276                 Roo.apply(d, {'roo-id' : Roo.id()});
18277                 
18278                 var _this = this;
18279             
18280                 Roo.each(this.parent.item, function(item){
18281                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18282                         return;
18283                     }
18284                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18285                 });
18286             }
18287             
18288             html[html.length] = Roo.util.Format.trim(
18289                 this.dataName ?
18290                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18291                     t.apply(d)
18292             );
18293         }
18294         
18295         
18296         
18297         el.update(html.join(""));
18298         this.nodes = el.dom.childNodes;
18299         this.updateIndexes(0);
18300     },
18301     
18302
18303     /**
18304      * Function to override to reformat the data that is sent to
18305      * the template for each node.
18306      * DEPRICATED - use the preparedata event handler.
18307      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18308      * a JSON object for an UpdateManager bound view).
18309      */
18310     prepareData : function(data, index, record)
18311     {
18312         this.fireEvent("preparedata", this, data, index, record);
18313         return data;
18314     },
18315
18316     onUpdate : function(ds, record){
18317         // Roo.log('on update');   
18318         this.clearSelections();
18319         var index = this.store.indexOf(record);
18320         var n = this.nodes[index];
18321         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18322         n.parentNode.removeChild(n);
18323         this.updateIndexes(index, index);
18324     },
18325
18326     
18327     
18328 // --------- FIXME     
18329     onAdd : function(ds, records, index)
18330     {
18331         //Roo.log(['on Add', ds, records, index] );        
18332         this.clearSelections();
18333         if(this.nodes.length == 0){
18334             this.refresh();
18335             return;
18336         }
18337         var n = this.nodes[index];
18338         for(var i = 0, len = records.length; i < len; i++){
18339             var d = this.prepareData(records[i].data, i, records[i]);
18340             if(n){
18341                 this.tpl.insertBefore(n, d);
18342             }else{
18343                 
18344                 this.tpl.append(this.el, d);
18345             }
18346         }
18347         this.updateIndexes(index);
18348     },
18349
18350     onRemove : function(ds, record, index){
18351        // Roo.log('onRemove');
18352         this.clearSelections();
18353         var el = this.dataName  ?
18354             this.el.child('.roo-tpl-' + this.dataName) :
18355             this.el; 
18356         
18357         el.dom.removeChild(this.nodes[index]);
18358         this.updateIndexes(index);
18359     },
18360
18361     /**
18362      * Refresh an individual node.
18363      * @param {Number} index
18364      */
18365     refreshNode : function(index){
18366         this.onUpdate(this.store, this.store.getAt(index));
18367     },
18368
18369     updateIndexes : function(startIndex, endIndex){
18370         var ns = this.nodes;
18371         startIndex = startIndex || 0;
18372         endIndex = endIndex || ns.length - 1;
18373         for(var i = startIndex; i <= endIndex; i++){
18374             ns[i].nodeIndex = i;
18375         }
18376     },
18377
18378     /**
18379      * Changes the data store this view uses and refresh the view.
18380      * @param {Store} store
18381      */
18382     setStore : function(store, initial){
18383         if(!initial && this.store){
18384             this.store.un("datachanged", this.refresh);
18385             this.store.un("add", this.onAdd);
18386             this.store.un("remove", this.onRemove);
18387             this.store.un("update", this.onUpdate);
18388             this.store.un("clear", this.refresh);
18389             this.store.un("beforeload", this.onBeforeLoad);
18390             this.store.un("load", this.onLoad);
18391             this.store.un("loadexception", this.onLoad);
18392         }
18393         if(store){
18394           
18395             store.on("datachanged", this.refresh, this);
18396             store.on("add", this.onAdd, this);
18397             store.on("remove", this.onRemove, this);
18398             store.on("update", this.onUpdate, this);
18399             store.on("clear", this.refresh, this);
18400             store.on("beforeload", this.onBeforeLoad, this);
18401             store.on("load", this.onLoad, this);
18402             store.on("loadexception", this.onLoad, this);
18403         }
18404         
18405         if(store){
18406             this.refresh();
18407         }
18408     },
18409     /**
18410      * onbeforeLoad - masks the loading area.
18411      *
18412      */
18413     onBeforeLoad : function(store,opts)
18414     {
18415          //Roo.log('onBeforeLoad');   
18416         if (!opts.add) {
18417             this.el.update("");
18418         }
18419         this.el.mask(this.mask ? this.mask : "Loading" ); 
18420     },
18421     onLoad : function ()
18422     {
18423         this.el.unmask();
18424     },
18425     
18426
18427     /**
18428      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18429      * @param {HTMLElement} node
18430      * @return {HTMLElement} The template node
18431      */
18432     findItemFromChild : function(node){
18433         var el = this.dataName  ?
18434             this.el.child('.roo-tpl-' + this.dataName,true) :
18435             this.el.dom; 
18436         
18437         if(!node || node.parentNode == el){
18438                     return node;
18439             }
18440             var p = node.parentNode;
18441             while(p && p != el){
18442             if(p.parentNode == el){
18443                 return p;
18444             }
18445             p = p.parentNode;
18446         }
18447             return null;
18448     },
18449
18450     /** @ignore */
18451     onClick : function(e){
18452         var item = this.findItemFromChild(e.getTarget());
18453         if(item){
18454             var index = this.indexOf(item);
18455             if(this.onItemClick(item, index, e) !== false){
18456                 this.fireEvent("click", this, index, item, e);
18457             }
18458         }else{
18459             this.clearSelections();
18460         }
18461     },
18462
18463     /** @ignore */
18464     onContextMenu : function(e){
18465         var item = this.findItemFromChild(e.getTarget());
18466         if(item){
18467             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18468         }
18469     },
18470
18471     /** @ignore */
18472     onDblClick : function(e){
18473         var item = this.findItemFromChild(e.getTarget());
18474         if(item){
18475             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18476         }
18477     },
18478
18479     onItemClick : function(item, index, e)
18480     {
18481         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18482             return false;
18483         }
18484         if (this.toggleSelect) {
18485             var m = this.isSelected(item) ? 'unselect' : 'select';
18486             //Roo.log(m);
18487             var _t = this;
18488             _t[m](item, true, false);
18489             return true;
18490         }
18491         if(this.multiSelect || this.singleSelect){
18492             if(this.multiSelect && e.shiftKey && this.lastSelection){
18493                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18494             }else{
18495                 this.select(item, this.multiSelect && e.ctrlKey);
18496                 this.lastSelection = item;
18497             }
18498             
18499             if(!this.tickable){
18500                 e.preventDefault();
18501             }
18502             
18503         }
18504         return true;
18505     },
18506
18507     /**
18508      * Get the number of selected nodes.
18509      * @return {Number}
18510      */
18511     getSelectionCount : function(){
18512         return this.selections.length;
18513     },
18514
18515     /**
18516      * Get the currently selected nodes.
18517      * @return {Array} An array of HTMLElements
18518      */
18519     getSelectedNodes : function(){
18520         return this.selections;
18521     },
18522
18523     /**
18524      * Get the indexes of the selected nodes.
18525      * @return {Array}
18526      */
18527     getSelectedIndexes : function(){
18528         var indexes = [], s = this.selections;
18529         for(var i = 0, len = s.length; i < len; i++){
18530             indexes.push(s[i].nodeIndex);
18531         }
18532         return indexes;
18533     },
18534
18535     /**
18536      * Clear all selections
18537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18538      */
18539     clearSelections : function(suppressEvent){
18540         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18541             this.cmp.elements = this.selections;
18542             this.cmp.removeClass(this.selectedClass);
18543             this.selections = [];
18544             if(!suppressEvent){
18545                 this.fireEvent("selectionchange", this, this.selections);
18546             }
18547         }
18548     },
18549
18550     /**
18551      * Returns true if the passed node is selected
18552      * @param {HTMLElement/Number} node The node or node index
18553      * @return {Boolean}
18554      */
18555     isSelected : function(node){
18556         var s = this.selections;
18557         if(s.length < 1){
18558             return false;
18559         }
18560         node = this.getNode(node);
18561         return s.indexOf(node) !== -1;
18562     },
18563
18564     /**
18565      * Selects nodes.
18566      * @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
18567      * @param {Boolean} keepExisting (optional) true to keep existing selections
18568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18569      */
18570     select : function(nodeInfo, keepExisting, suppressEvent){
18571         if(nodeInfo instanceof Array){
18572             if(!keepExisting){
18573                 this.clearSelections(true);
18574             }
18575             for(var i = 0, len = nodeInfo.length; i < len; i++){
18576                 this.select(nodeInfo[i], true, true);
18577             }
18578             return;
18579         } 
18580         var node = this.getNode(nodeInfo);
18581         if(!node || this.isSelected(node)){
18582             return; // already selected.
18583         }
18584         if(!keepExisting){
18585             this.clearSelections(true);
18586         }
18587         
18588         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18589             Roo.fly(node).addClass(this.selectedClass);
18590             this.selections.push(node);
18591             if(!suppressEvent){
18592                 this.fireEvent("selectionchange", this, this.selections);
18593             }
18594         }
18595         
18596         
18597     },
18598       /**
18599      * Unselects nodes.
18600      * @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
18601      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18603      */
18604     unselect : function(nodeInfo, keepExisting, suppressEvent)
18605     {
18606         if(nodeInfo instanceof Array){
18607             Roo.each(this.selections, function(s) {
18608                 this.unselect(s, nodeInfo);
18609             }, this);
18610             return;
18611         }
18612         var node = this.getNode(nodeInfo);
18613         if(!node || !this.isSelected(node)){
18614             //Roo.log("not selected");
18615             return; // not selected.
18616         }
18617         // fireevent???
18618         var ns = [];
18619         Roo.each(this.selections, function(s) {
18620             if (s == node ) {
18621                 Roo.fly(node).removeClass(this.selectedClass);
18622
18623                 return;
18624             }
18625             ns.push(s);
18626         },this);
18627         
18628         this.selections= ns;
18629         this.fireEvent("selectionchange", this, this.selections);
18630     },
18631
18632     /**
18633      * Gets a template node.
18634      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18635      * @return {HTMLElement} The node or null if it wasn't found
18636      */
18637     getNode : function(nodeInfo){
18638         if(typeof nodeInfo == "string"){
18639             return document.getElementById(nodeInfo);
18640         }else if(typeof nodeInfo == "number"){
18641             return this.nodes[nodeInfo];
18642         }
18643         return nodeInfo;
18644     },
18645
18646     /**
18647      * Gets a range template nodes.
18648      * @param {Number} startIndex
18649      * @param {Number} endIndex
18650      * @return {Array} An array of nodes
18651      */
18652     getNodes : function(start, end){
18653         var ns = this.nodes;
18654         start = start || 0;
18655         end = typeof end == "undefined" ? ns.length - 1 : end;
18656         var nodes = [];
18657         if(start <= end){
18658             for(var i = start; i <= end; i++){
18659                 nodes.push(ns[i]);
18660             }
18661         } else{
18662             for(var i = start; i >= end; i--){
18663                 nodes.push(ns[i]);
18664             }
18665         }
18666         return nodes;
18667     },
18668
18669     /**
18670      * Finds the index of the passed node
18671      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18672      * @return {Number} The index of the node or -1
18673      */
18674     indexOf : function(node){
18675         node = this.getNode(node);
18676         if(typeof node.nodeIndex == "number"){
18677             return node.nodeIndex;
18678         }
18679         var ns = this.nodes;
18680         for(var i = 0, len = ns.length; i < len; i++){
18681             if(ns[i] == node){
18682                 return i;
18683             }
18684         }
18685         return -1;
18686     }
18687 });
18688 /*
18689  * - LGPL
18690  *
18691  * based on jquery fullcalendar
18692  * 
18693  */
18694
18695 Roo.bootstrap = Roo.bootstrap || {};
18696 /**
18697  * @class Roo.bootstrap.Calendar
18698  * @extends Roo.bootstrap.Component
18699  * Bootstrap Calendar class
18700  * @cfg {Boolean} loadMask (true|false) default false
18701  * @cfg {Object} header generate the user specific header of the calendar, default false
18702
18703  * @constructor
18704  * Create a new Container
18705  * @param {Object} config The config object
18706  */
18707
18708
18709
18710 Roo.bootstrap.Calendar = function(config){
18711     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18712      this.addEvents({
18713         /**
18714              * @event select
18715              * Fires when a date is selected
18716              * @param {DatePicker} this
18717              * @param {Date} date The selected date
18718              */
18719         'select': true,
18720         /**
18721              * @event monthchange
18722              * Fires when the displayed month changes 
18723              * @param {DatePicker} this
18724              * @param {Date} date The selected month
18725              */
18726         'monthchange': true,
18727         /**
18728              * @event evententer
18729              * Fires when mouse over an event
18730              * @param {Calendar} this
18731              * @param {event} Event
18732              */
18733         'evententer': true,
18734         /**
18735              * @event eventleave
18736              * Fires when the mouse leaves an
18737              * @param {Calendar} this
18738              * @param {event}
18739              */
18740         'eventleave': true,
18741         /**
18742              * @event eventclick
18743              * Fires when the mouse click an
18744              * @param {Calendar} this
18745              * @param {event}
18746              */
18747         'eventclick': true
18748         
18749     });
18750
18751 };
18752
18753 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18754     
18755      /**
18756      * @cfg {Number} startDay
18757      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18758      */
18759     startDay : 0,
18760     
18761     loadMask : false,
18762     
18763     header : false,
18764       
18765     getAutoCreate : function(){
18766         
18767         
18768         var fc_button = function(name, corner, style, content ) {
18769             return Roo.apply({},{
18770                 tag : 'span',
18771                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18772                          (corner.length ?
18773                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18774                             ''
18775                         ),
18776                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18777                 unselectable: 'on'
18778             });
18779         };
18780         
18781         var header = {};
18782         
18783         if(!this.header){
18784             header = {
18785                 tag : 'table',
18786                 cls : 'fc-header',
18787                 style : 'width:100%',
18788                 cn : [
18789                     {
18790                         tag: 'tr',
18791                         cn : [
18792                             {
18793                                 tag : 'td',
18794                                 cls : 'fc-header-left',
18795                                 cn : [
18796                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18797                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18798                                     { tag: 'span', cls: 'fc-header-space' },
18799                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18800
18801
18802                                 ]
18803                             },
18804
18805                             {
18806                                 tag : 'td',
18807                                 cls : 'fc-header-center',
18808                                 cn : [
18809                                     {
18810                                         tag: 'span',
18811                                         cls: 'fc-header-title',
18812                                         cn : {
18813                                             tag: 'H2',
18814                                             html : 'month / year'
18815                                         }
18816                                     }
18817
18818                                 ]
18819                             },
18820                             {
18821                                 tag : 'td',
18822                                 cls : 'fc-header-right',
18823                                 cn : [
18824                               /*      fc_button('month', 'left', '', 'month' ),
18825                                     fc_button('week', '', '', 'week' ),
18826                                     fc_button('day', 'right', '', 'day' )
18827                                 */    
18828
18829                                 ]
18830                             }
18831
18832                         ]
18833                     }
18834                 ]
18835             };
18836         }
18837         
18838         header = this.header;
18839         
18840        
18841         var cal_heads = function() {
18842             var ret = [];
18843             // fixme - handle this.
18844             
18845             for (var i =0; i < Date.dayNames.length; i++) {
18846                 var d = Date.dayNames[i];
18847                 ret.push({
18848                     tag: 'th',
18849                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18850                     html : d.substring(0,3)
18851                 });
18852                 
18853             }
18854             ret[0].cls += ' fc-first';
18855             ret[6].cls += ' fc-last';
18856             return ret;
18857         };
18858         var cal_cell = function(n) {
18859             return  {
18860                 tag: 'td',
18861                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18862                 cn : [
18863                     {
18864                         cn : [
18865                             {
18866                                 cls: 'fc-day-number',
18867                                 html: 'D'
18868                             },
18869                             {
18870                                 cls: 'fc-day-content',
18871                              
18872                                 cn : [
18873                                      {
18874                                         style: 'position: relative;' // height: 17px;
18875                                     }
18876                                 ]
18877                             }
18878                             
18879                             
18880                         ]
18881                     }
18882                 ]
18883                 
18884             }
18885         };
18886         var cal_rows = function() {
18887             
18888             var ret = [];
18889             for (var r = 0; r < 6; r++) {
18890                 var row= {
18891                     tag : 'tr',
18892                     cls : 'fc-week',
18893                     cn : []
18894                 };
18895                 
18896                 for (var i =0; i < Date.dayNames.length; i++) {
18897                     var d = Date.dayNames[i];
18898                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18899
18900                 }
18901                 row.cn[0].cls+=' fc-first';
18902                 row.cn[0].cn[0].style = 'min-height:90px';
18903                 row.cn[6].cls+=' fc-last';
18904                 ret.push(row);
18905                 
18906             }
18907             ret[0].cls += ' fc-first';
18908             ret[4].cls += ' fc-prev-last';
18909             ret[5].cls += ' fc-last';
18910             return ret;
18911             
18912         };
18913         
18914         var cal_table = {
18915             tag: 'table',
18916             cls: 'fc-border-separate',
18917             style : 'width:100%',
18918             cellspacing  : 0,
18919             cn : [
18920                 { 
18921                     tag: 'thead',
18922                     cn : [
18923                         { 
18924                             tag: 'tr',
18925                             cls : 'fc-first fc-last',
18926                             cn : cal_heads()
18927                         }
18928                     ]
18929                 },
18930                 { 
18931                     tag: 'tbody',
18932                     cn : cal_rows()
18933                 }
18934                   
18935             ]
18936         };
18937          
18938          var cfg = {
18939             cls : 'fc fc-ltr',
18940             cn : [
18941                 header,
18942                 {
18943                     cls : 'fc-content',
18944                     style : "position: relative;",
18945                     cn : [
18946                         {
18947                             cls : 'fc-view fc-view-month fc-grid',
18948                             style : 'position: relative',
18949                             unselectable : 'on',
18950                             cn : [
18951                                 {
18952                                     cls : 'fc-event-container',
18953                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18954                                 },
18955                                 cal_table
18956                             ]
18957                         }
18958                     ]
18959     
18960                 }
18961            ] 
18962             
18963         };
18964         
18965          
18966         
18967         return cfg;
18968     },
18969     
18970     
18971     initEvents : function()
18972     {
18973         if(!this.store){
18974             throw "can not find store for calendar";
18975         }
18976         
18977         var mark = {
18978             tag: "div",
18979             cls:"x-dlg-mask",
18980             style: "text-align:center",
18981             cn: [
18982                 {
18983                     tag: "div",
18984                     style: "background-color:white;width:50%;margin:250 auto",
18985                     cn: [
18986                         {
18987                             tag: "img",
18988                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18989                         },
18990                         {
18991                             tag: "span",
18992                             html: "Loading"
18993                         }
18994                         
18995                     ]
18996                 }
18997             ]
18998         };
18999         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19000         
19001         var size = this.el.select('.fc-content', true).first().getSize();
19002         this.maskEl.setSize(size.width, size.height);
19003         this.maskEl.enableDisplayMode("block");
19004         if(!this.loadMask){
19005             this.maskEl.hide();
19006         }
19007         
19008         this.store = Roo.factory(this.store, Roo.data);
19009         this.store.on('load', this.onLoad, this);
19010         this.store.on('beforeload', this.onBeforeLoad, this);
19011         
19012         this.resize();
19013         
19014         this.cells = this.el.select('.fc-day',true);
19015         //Roo.log(this.cells);
19016         this.textNodes = this.el.query('.fc-day-number');
19017         this.cells.addClassOnOver('fc-state-hover');
19018         
19019         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19020         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19021         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19022         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19023         
19024         this.on('monthchange', this.onMonthChange, this);
19025         
19026         this.update(new Date().clearTime());
19027     },
19028     
19029     resize : function() {
19030         var sz  = this.el.getSize();
19031         
19032         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19033         this.el.select('.fc-day-content div',true).setHeight(34);
19034     },
19035     
19036     
19037     // private
19038     showPrevMonth : function(e){
19039         this.update(this.activeDate.add("mo", -1));
19040     },
19041     showToday : function(e){
19042         this.update(new Date().clearTime());
19043     },
19044     // private
19045     showNextMonth : function(e){
19046         this.update(this.activeDate.add("mo", 1));
19047     },
19048
19049     // private
19050     showPrevYear : function(){
19051         this.update(this.activeDate.add("y", -1));
19052     },
19053
19054     // private
19055     showNextYear : function(){
19056         this.update(this.activeDate.add("y", 1));
19057     },
19058
19059     
19060    // private
19061     update : function(date)
19062     {
19063         var vd = this.activeDate;
19064         this.activeDate = date;
19065 //        if(vd && this.el){
19066 //            var t = date.getTime();
19067 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19068 //                Roo.log('using add remove');
19069 //                
19070 //                this.fireEvent('monthchange', this, date);
19071 //                
19072 //                this.cells.removeClass("fc-state-highlight");
19073 //                this.cells.each(function(c){
19074 //                   if(c.dateValue == t){
19075 //                       c.addClass("fc-state-highlight");
19076 //                       setTimeout(function(){
19077 //                            try{c.dom.firstChild.focus();}catch(e){}
19078 //                       }, 50);
19079 //                       return false;
19080 //                   }
19081 //                   return true;
19082 //                });
19083 //                return;
19084 //            }
19085 //        }
19086         
19087         var days = date.getDaysInMonth();
19088         
19089         var firstOfMonth = date.getFirstDateOfMonth();
19090         var startingPos = firstOfMonth.getDay()-this.startDay;
19091         
19092         if(startingPos < this.startDay){
19093             startingPos += 7;
19094         }
19095         
19096         var pm = date.add(Date.MONTH, -1);
19097         var prevStart = pm.getDaysInMonth()-startingPos;
19098 //        
19099         this.cells = this.el.select('.fc-day',true);
19100         this.textNodes = this.el.query('.fc-day-number');
19101         this.cells.addClassOnOver('fc-state-hover');
19102         
19103         var cells = this.cells.elements;
19104         var textEls = this.textNodes;
19105         
19106         Roo.each(cells, function(cell){
19107             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19108         });
19109         
19110         days += startingPos;
19111
19112         // convert everything to numbers so it's fast
19113         var day = 86400000;
19114         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19115         //Roo.log(d);
19116         //Roo.log(pm);
19117         //Roo.log(prevStart);
19118         
19119         var today = new Date().clearTime().getTime();
19120         var sel = date.clearTime().getTime();
19121         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19122         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19123         var ddMatch = this.disabledDatesRE;
19124         var ddText = this.disabledDatesText;
19125         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19126         var ddaysText = this.disabledDaysText;
19127         var format = this.format;
19128         
19129         var setCellClass = function(cal, cell){
19130             cell.row = 0;
19131             cell.events = [];
19132             cell.more = [];
19133             //Roo.log('set Cell Class');
19134             cell.title = "";
19135             var t = d.getTime();
19136             
19137             //Roo.log(d);
19138             
19139             cell.dateValue = t;
19140             if(t == today){
19141                 cell.className += " fc-today";
19142                 cell.className += " fc-state-highlight";
19143                 cell.title = cal.todayText;
19144             }
19145             if(t == sel){
19146                 // disable highlight in other month..
19147                 //cell.className += " fc-state-highlight";
19148                 
19149             }
19150             // disabling
19151             if(t < min) {
19152                 cell.className = " fc-state-disabled";
19153                 cell.title = cal.minText;
19154                 return;
19155             }
19156             if(t > max) {
19157                 cell.className = " fc-state-disabled";
19158                 cell.title = cal.maxText;
19159                 return;
19160             }
19161             if(ddays){
19162                 if(ddays.indexOf(d.getDay()) != -1){
19163                     cell.title = ddaysText;
19164                     cell.className = " fc-state-disabled";
19165                 }
19166             }
19167             if(ddMatch && format){
19168                 var fvalue = d.dateFormat(format);
19169                 if(ddMatch.test(fvalue)){
19170                     cell.title = ddText.replace("%0", fvalue);
19171                     cell.className = " fc-state-disabled";
19172                 }
19173             }
19174             
19175             if (!cell.initialClassName) {
19176                 cell.initialClassName = cell.dom.className;
19177             }
19178             
19179             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19180         };
19181
19182         var i = 0;
19183         
19184         for(; i < startingPos; i++) {
19185             textEls[i].innerHTML = (++prevStart);
19186             d.setDate(d.getDate()+1);
19187             
19188             cells[i].className = "fc-past fc-other-month";
19189             setCellClass(this, cells[i]);
19190         }
19191         
19192         var intDay = 0;
19193         
19194         for(; i < days; i++){
19195             intDay = i - startingPos + 1;
19196             textEls[i].innerHTML = (intDay);
19197             d.setDate(d.getDate()+1);
19198             
19199             cells[i].className = ''; // "x-date-active";
19200             setCellClass(this, cells[i]);
19201         }
19202         var extraDays = 0;
19203         
19204         for(; i < 42; i++) {
19205             textEls[i].innerHTML = (++extraDays);
19206             d.setDate(d.getDate()+1);
19207             
19208             cells[i].className = "fc-future fc-other-month";
19209             setCellClass(this, cells[i]);
19210         }
19211         
19212         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19213         
19214         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19215         
19216         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19217         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19218         
19219         if(totalRows != 6){
19220             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19221             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19222         }
19223         
19224         this.fireEvent('monthchange', this, date);
19225         
19226         
19227         /*
19228         if(!this.internalRender){
19229             var main = this.el.dom.firstChild;
19230             var w = main.offsetWidth;
19231             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19232             Roo.fly(main).setWidth(w);
19233             this.internalRender = true;
19234             // opera does not respect the auto grow header center column
19235             // then, after it gets a width opera refuses to recalculate
19236             // without a second pass
19237             if(Roo.isOpera && !this.secondPass){
19238                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19239                 this.secondPass = true;
19240                 this.update.defer(10, this, [date]);
19241             }
19242         }
19243         */
19244         
19245     },
19246     
19247     findCell : function(dt) {
19248         dt = dt.clearTime().getTime();
19249         var ret = false;
19250         this.cells.each(function(c){
19251             //Roo.log("check " +c.dateValue + '?=' + dt);
19252             if(c.dateValue == dt){
19253                 ret = c;
19254                 return false;
19255             }
19256             return true;
19257         });
19258         
19259         return ret;
19260     },
19261     
19262     findCells : function(ev) {
19263         var s = ev.start.clone().clearTime().getTime();
19264        // Roo.log(s);
19265         var e= ev.end.clone().clearTime().getTime();
19266        // Roo.log(e);
19267         var ret = [];
19268         this.cells.each(function(c){
19269              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19270             
19271             if(c.dateValue > e){
19272                 return ;
19273             }
19274             if(c.dateValue < s){
19275                 return ;
19276             }
19277             ret.push(c);
19278         });
19279         
19280         return ret;    
19281     },
19282     
19283 //    findBestRow: function(cells)
19284 //    {
19285 //        var ret = 0;
19286 //        
19287 //        for (var i =0 ; i < cells.length;i++) {
19288 //            ret  = Math.max(cells[i].rows || 0,ret);
19289 //        }
19290 //        return ret;
19291 //        
19292 //    },
19293     
19294     
19295     addItem : function(ev)
19296     {
19297         // look for vertical location slot in
19298         var cells = this.findCells(ev);
19299         
19300 //        ev.row = this.findBestRow(cells);
19301         
19302         // work out the location.
19303         
19304         var crow = false;
19305         var rows = [];
19306         for(var i =0; i < cells.length; i++) {
19307             
19308             cells[i].row = cells[0].row;
19309             
19310             if(i == 0){
19311                 cells[i].row = cells[i].row + 1;
19312             }
19313             
19314             if (!crow) {
19315                 crow = {
19316                     start : cells[i],
19317                     end :  cells[i]
19318                 };
19319                 continue;
19320             }
19321             if (crow.start.getY() == cells[i].getY()) {
19322                 // on same row.
19323                 crow.end = cells[i];
19324                 continue;
19325             }
19326             // different row.
19327             rows.push(crow);
19328             crow = {
19329                 start: cells[i],
19330                 end : cells[i]
19331             };
19332             
19333         }
19334         
19335         rows.push(crow);
19336         ev.els = [];
19337         ev.rows = rows;
19338         ev.cells = cells;
19339         
19340         cells[0].events.push(ev);
19341         
19342         this.calevents.push(ev);
19343     },
19344     
19345     clearEvents: function() {
19346         
19347         if(!this.calevents){
19348             return;
19349         }
19350         
19351         Roo.each(this.cells.elements, function(c){
19352             c.row = 0;
19353             c.events = [];
19354             c.more = [];
19355         });
19356         
19357         Roo.each(this.calevents, function(e) {
19358             Roo.each(e.els, function(el) {
19359                 el.un('mouseenter' ,this.onEventEnter, this);
19360                 el.un('mouseleave' ,this.onEventLeave, this);
19361                 el.remove();
19362             },this);
19363         },this);
19364         
19365         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19366             e.remove();
19367         });
19368         
19369     },
19370     
19371     renderEvents: function()
19372     {   
19373         var _this = this;
19374         
19375         this.cells.each(function(c) {
19376             
19377             if(c.row < 5){
19378                 return;
19379             }
19380             
19381             var ev = c.events;
19382             
19383             var r = 4;
19384             if(c.row != c.events.length){
19385                 r = 4 - (4 - (c.row - c.events.length));
19386             }
19387             
19388             c.events = ev.slice(0, r);
19389             c.more = ev.slice(r);
19390             
19391             if(c.more.length && c.more.length == 1){
19392                 c.events.push(c.more.pop());
19393             }
19394             
19395             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19396             
19397         });
19398             
19399         this.cells.each(function(c) {
19400             
19401             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19402             
19403             
19404             for (var e = 0; e < c.events.length; e++){
19405                 var ev = c.events[e];
19406                 var rows = ev.rows;
19407                 
19408                 for(var i = 0; i < rows.length; i++) {
19409                 
19410                     // how many rows should it span..
19411
19412                     var  cfg = {
19413                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19414                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19415
19416                         unselectable : "on",
19417                         cn : [
19418                             {
19419                                 cls: 'fc-event-inner',
19420                                 cn : [
19421     //                                {
19422     //                                  tag:'span',
19423     //                                  cls: 'fc-event-time',
19424     //                                  html : cells.length > 1 ? '' : ev.time
19425     //                                },
19426                                     {
19427                                       tag:'span',
19428                                       cls: 'fc-event-title',
19429                                       html : String.format('{0}', ev.title)
19430                                     }
19431
19432
19433                                 ]
19434                             },
19435                             {
19436                                 cls: 'ui-resizable-handle ui-resizable-e',
19437                                 html : '&nbsp;&nbsp;&nbsp'
19438                             }
19439
19440                         ]
19441                     };
19442
19443                     if (i == 0) {
19444                         cfg.cls += ' fc-event-start';
19445                     }
19446                     if ((i+1) == rows.length) {
19447                         cfg.cls += ' fc-event-end';
19448                     }
19449
19450                     var ctr = _this.el.select('.fc-event-container',true).first();
19451                     var cg = ctr.createChild(cfg);
19452
19453                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19454                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19455
19456                     var r = (c.more.length) ? 1 : 0;
19457                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19458                     cg.setWidth(ebox.right - sbox.x -2);
19459
19460                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19461                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19462                     cg.on('click', _this.onEventClick, _this, ev);
19463
19464                     ev.els.push(cg);
19465                     
19466                 }
19467                 
19468             }
19469             
19470             
19471             if(c.more.length){
19472                 var  cfg = {
19473                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19474                     style : 'position: absolute',
19475                     unselectable : "on",
19476                     cn : [
19477                         {
19478                             cls: 'fc-event-inner',
19479                             cn : [
19480                                 {
19481                                   tag:'span',
19482                                   cls: 'fc-event-title',
19483                                   html : 'More'
19484                                 }
19485
19486
19487                             ]
19488                         },
19489                         {
19490                             cls: 'ui-resizable-handle ui-resizable-e',
19491                             html : '&nbsp;&nbsp;&nbsp'
19492                         }
19493
19494                     ]
19495                 };
19496
19497                 var ctr = _this.el.select('.fc-event-container',true).first();
19498                 var cg = ctr.createChild(cfg);
19499
19500                 var sbox = c.select('.fc-day-content',true).first().getBox();
19501                 var ebox = c.select('.fc-day-content',true).first().getBox();
19502                 //Roo.log(cg);
19503                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19504                 cg.setWidth(ebox.right - sbox.x -2);
19505
19506                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19507                 
19508             }
19509             
19510         });
19511         
19512         
19513         
19514     },
19515     
19516     onEventEnter: function (e, el,event,d) {
19517         this.fireEvent('evententer', this, el, event);
19518     },
19519     
19520     onEventLeave: function (e, el,event,d) {
19521         this.fireEvent('eventleave', this, el, event);
19522     },
19523     
19524     onEventClick: function (e, el,event,d) {
19525         this.fireEvent('eventclick', this, el, event);
19526     },
19527     
19528     onMonthChange: function () {
19529         this.store.load();
19530     },
19531     
19532     onMoreEventClick: function(e, el, more)
19533     {
19534         var _this = this;
19535         
19536         this.calpopover.placement = 'right';
19537         this.calpopover.setTitle('More');
19538         
19539         this.calpopover.setContent('');
19540         
19541         var ctr = this.calpopover.el.select('.popover-content', true).first();
19542         
19543         Roo.each(more, function(m){
19544             var cfg = {
19545                 cls : 'fc-event-hori fc-event-draggable',
19546                 html : m.title
19547             };
19548             var cg = ctr.createChild(cfg);
19549             
19550             cg.on('click', _this.onEventClick, _this, m);
19551         });
19552         
19553         this.calpopover.show(el);
19554         
19555         
19556     },
19557     
19558     onLoad: function () 
19559     {   
19560         this.calevents = [];
19561         var cal = this;
19562         
19563         if(this.store.getCount() > 0){
19564             this.store.data.each(function(d){
19565                cal.addItem({
19566                     id : d.data.id,
19567                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19568                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19569                     time : d.data.start_time,
19570                     title : d.data.title,
19571                     description : d.data.description,
19572                     venue : d.data.venue
19573                 });
19574             });
19575         }
19576         
19577         this.renderEvents();
19578         
19579         if(this.calevents.length && this.loadMask){
19580             this.maskEl.hide();
19581         }
19582     },
19583     
19584     onBeforeLoad: function()
19585     {
19586         this.clearEvents();
19587         if(this.loadMask){
19588             this.maskEl.show();
19589         }
19590     }
19591 });
19592
19593  
19594  /*
19595  * - LGPL
19596  *
19597  * element
19598  * 
19599  */
19600
19601 /**
19602  * @class Roo.bootstrap.Popover
19603  * @extends Roo.bootstrap.Component
19604  * Bootstrap Popover class
19605  * @cfg {String} html contents of the popover   (or false to use children..)
19606  * @cfg {String} title of popover (or false to hide)
19607  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19608  * @cfg {String} trigger click || hover (or false to trigger manually)
19609  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19610  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19611  *      - if false and it has a 'parent' then it will be automatically added to that element
19612  *      - if string - Roo.get  will be called 
19613  * @cfg {Number} delay - delay before showing
19614  
19615  * @constructor
19616  * Create a new Popover
19617  * @param {Object} config The config object
19618  */
19619
19620 Roo.bootstrap.Popover = function(config){
19621     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19622     
19623     this.addEvents({
19624         // raw events
19625          /**
19626          * @event show
19627          * After the popover show
19628          * 
19629          * @param {Roo.bootstrap.Popover} this
19630          */
19631         "show" : true,
19632         /**
19633          * @event hide
19634          * After the popover hide
19635          * 
19636          * @param {Roo.bootstrap.Popover} this
19637          */
19638         "hide" : true
19639     });
19640 };
19641
19642 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19643     
19644     title: false,
19645     html: false,
19646     
19647     placement : 'right',
19648     trigger : 'hover', // hover
19649     modal : false,
19650     delay : 0,
19651     
19652     over: false,
19653     
19654     can_build_overlaid : false,
19655     
19656     maskEl : false, // the mask element
19657     headerEl : false,
19658     contentEl : false,
19659     
19660     
19661     getChildContainer : function()
19662     {
19663         return this.contentEl;
19664         
19665     },
19666     getPopoverHeader : function()
19667     {
19668         this.title = true; // flag not to hide it..
19669         return this.headerEl
19670     },
19671     
19672     
19673     getAutoCreate : function(){
19674          
19675         var cfg = {
19676            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19677            style: 'display:block',
19678            cn : [
19679                 {
19680                     cls : 'arrow'
19681                 },
19682                 {
19683                     cls : 'popover-inner ',
19684                     cn : [
19685                         {
19686                             tag: 'h3',
19687                             cls: 'popover-title popover-header',
19688                             html : this.title === false ? '' : this.title
19689                         },
19690                         {
19691                             cls : 'popover-content popover-body '  + (this.cls || ''),
19692                             html : this.html || ''
19693                         }
19694                     ]
19695                     
19696                 }
19697            ]
19698         };
19699         
19700         return cfg;
19701     },
19702     /**
19703      * @param {string} the title
19704      */
19705     setTitle: function(str)
19706     {
19707         this.title = str;
19708         if (this.el) {
19709             this.headerEl.dom.innerHTML = str;
19710         }
19711         
19712     },
19713     /**
19714      * @param {string} the body content
19715      */
19716     setContent: function(str)
19717     {
19718         this.html = str;
19719         if (this.contentEl) {
19720             this.contentEl.dom.innerHTML = str;
19721         }
19722         
19723     },
19724     // as it get's added to the bottom of the page.
19725     onRender : function(ct, position)
19726     {
19727         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19728         
19729         
19730         
19731         if(!this.el){
19732             var cfg = Roo.apply({},  this.getAutoCreate());
19733             cfg.id = Roo.id();
19734             
19735             if (this.cls) {
19736                 cfg.cls += ' ' + this.cls;
19737             }
19738             if (this.style) {
19739                 cfg.style = this.style;
19740             }
19741             //Roo.log("adding to ");
19742             this.el = Roo.get(document.body).createChild(cfg, position);
19743 //            Roo.log(this.el);
19744         }
19745         
19746         this.contentEl = this.el.select('.popover-content',true).first();
19747         this.headerEl =  this.el.select('.popover-title',true).first();
19748         
19749         var nitems = [];
19750         if(typeof(this.items) != 'undefined'){
19751             var items = this.items;
19752             delete this.items;
19753
19754             for(var i =0;i < items.length;i++) {
19755                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19756             }
19757         }
19758
19759         this.items = nitems;
19760         
19761         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19762         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19763         
19764         
19765         
19766         this.initEvents();
19767     },
19768     
19769     resizeMask : function()
19770     {
19771         this.maskEl.setSize(
19772             Roo.lib.Dom.getViewWidth(true),
19773             Roo.lib.Dom.getViewHeight(true)
19774         );
19775     },
19776     
19777     initEvents : function()
19778     {
19779         
19780         if (!this.modal) { 
19781             Roo.bootstrap.Popover.register(this);
19782         }
19783          
19784         
19785         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19786         this.el.enableDisplayMode('block');
19787         this.el.hide();
19788         if (this.over === false && !this.parent()) {
19789             return; 
19790         }
19791         if (this.triggers === false) {
19792             return;
19793         }
19794          
19795         // support parent
19796         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19797         var triggers = this.trigger ? this.trigger.split(' ') : [];
19798         Roo.each(triggers, function(trigger) {
19799         
19800             if (trigger == 'click') {
19801                 on_el.on('click', this.toggle, this);
19802             } else if (trigger != 'manual') {
19803                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19804                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19805       
19806                 on_el.on(eventIn  ,this.enter, this);
19807                 on_el.on(eventOut, this.leave, this);
19808             }
19809         }, this);
19810         
19811     },
19812     
19813     
19814     // private
19815     timeout : null,
19816     hoverState : null,
19817     
19818     toggle : function () {
19819         this.hoverState == 'in' ? this.leave() : this.enter();
19820     },
19821     
19822     enter : function () {
19823         
19824         clearTimeout(this.timeout);
19825     
19826         this.hoverState = 'in';
19827     
19828         if (!this.delay || !this.delay.show) {
19829             this.show();
19830             return;
19831         }
19832         var _t = this;
19833         this.timeout = setTimeout(function () {
19834             if (_t.hoverState == 'in') {
19835                 _t.show();
19836             }
19837         }, this.delay.show)
19838     },
19839     
19840     leave : function() {
19841         clearTimeout(this.timeout);
19842     
19843         this.hoverState = 'out';
19844     
19845         if (!this.delay || !this.delay.hide) {
19846             this.hide();
19847             return;
19848         }
19849         var _t = this;
19850         this.timeout = setTimeout(function () {
19851             if (_t.hoverState == 'out') {
19852                 _t.hide();
19853             }
19854         }, this.delay.hide)
19855     },
19856     /**
19857      * Show the popover
19858      * @param {Roo.Element|string|false} - element to align and point to.
19859      */
19860     show : function (on_el)
19861     {
19862         
19863         on_el = on_el || false; // default to false
19864         if (!on_el) {
19865             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19866                 on_el = this.parent().el;
19867             } else if (this.over) {
19868                 Roo.get(this.over);
19869             }
19870             
19871         }
19872         
19873         if (!this.el) {
19874             this.render(document.body);
19875         }
19876         
19877         
19878         this.el.removeClass([
19879             'fade','top','bottom', 'left', 'right','in',
19880             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19881         ]);
19882         
19883         if (this.title === false) {
19884             this.headerEl.hide();
19885         }
19886         
19887         
19888         var placement = typeof this.placement == 'function' ?
19889             this.placement.call(this, this.el, on_el) :
19890             this.placement;
19891             
19892         /*
19893         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19894         
19895         // I think  'auto right' - but 
19896         
19897         var autoPlace = autoToken.test(placement);
19898         if (autoPlace) {
19899             placement = placement.replace(autoToken, '') || 'top';
19900         }
19901         */
19902         
19903         
19904         this.el.show();
19905         this.el.dom.style.display='block';
19906         
19907         //this.el.appendTo(on_el);
19908         
19909         var p = this.getPosition();
19910         var box = this.el.getBox();
19911         
19912         
19913         var align = Roo.bootstrap.Popover.alignment[placement];
19914         this.el.addClass(align[2]);
19915
19916 //        Roo.log(align);
19917
19918         if (on_el) {
19919             this.el.alignTo(on_el, align[0],align[1]);
19920         } else {
19921             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19922             var es = this.el.getSize();
19923             var x = Roo.lib.Dom.getViewWidth()/2;
19924             var y = Roo.lib.Dom.getViewHeight()/2;
19925             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19926             
19927         }
19928
19929         
19930         //var arrow = this.el.select('.arrow',true).first();
19931         //arrow.set(align[2], 
19932         
19933         this.el.addClass('in');
19934         
19935         
19936         if (this.el.hasClass('fade')) {
19937             // fade it?
19938         }
19939         
19940         this.hoverState = 'in';
19941         
19942         if (this.modal) {
19943             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19944             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945             this.maskEl.dom.style.display = 'block';
19946             this.maskEl.addClass('show');
19947         }
19948         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19949
19950         
19951         
19952         this.fireEvent('show', this);
19953         
19954     },
19955     hide : function()
19956     {
19957         this.el.setXY([0,0]);
19958         this.el.removeClass('in');
19959         this.el.hide();
19960         this.hoverState = null;
19961         this.maskEl.hide(); // always..
19962         this.fireEvent('hide', this);
19963     }
19964     
19965 });
19966
19967
19968 Roo.apply(Roo.bootstrap.Popover, {
19969
19970     alignment : {
19971         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19972         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19973         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19974         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19975     },
19976     
19977     zIndex : 20001,
19978
19979     clickHander : false,
19980     
19981
19982     onMouseDown : function(e)
19983     {
19984         if (!e.getTarget(".roo-popover")) {
19985             this.hideAll();
19986         }
19987          
19988     },
19989     
19990     popups : [],
19991     
19992     register : function(popup)
19993     {
19994         if (!Roo.bootstrap.Popover.clickHandler) {
19995             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19996         }
19997         // hide other popups.
19998         this.hideAll();
19999         this.popups.push(popup);
20000     },
20001     hideAll : function()
20002     {
20003         this.popups.forEach(function(p) {
20004             p.hide();
20005         });
20006     }
20007
20008 });/*
20009  * - LGPL
20010  *
20011  * Card header - holder for the card header elements.
20012  * 
20013  */
20014
20015 /**
20016  * @class Roo.bootstrap.PopoverNav
20017  * @extends Roo.bootstrap.NavGroup
20018  * Bootstrap Popover header navigation class
20019  * @constructor
20020  * Create a new Popover Header Navigation 
20021  * @param {Object} config The config object
20022  */
20023
20024 Roo.bootstrap.PopoverNav = function(config){
20025     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20026 };
20027
20028 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20029     
20030     
20031     container_method : 'getPopoverHeader' 
20032     
20033      
20034     
20035     
20036    
20037 });
20038
20039  
20040
20041  /*
20042  * - LGPL
20043  *
20044  * Progress
20045  * 
20046  */
20047
20048 /**
20049  * @class Roo.bootstrap.Progress
20050  * @extends Roo.bootstrap.Component
20051  * Bootstrap Progress class
20052  * @cfg {Boolean} striped striped of the progress bar
20053  * @cfg {Boolean} active animated of the progress bar
20054  * 
20055  * 
20056  * @constructor
20057  * Create a new Progress
20058  * @param {Object} config The config object
20059  */
20060
20061 Roo.bootstrap.Progress = function(config){
20062     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20063 };
20064
20065 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20066     
20067     striped : false,
20068     active: false,
20069     
20070     getAutoCreate : function(){
20071         var cfg = {
20072             tag: 'div',
20073             cls: 'progress'
20074         };
20075         
20076         
20077         if(this.striped){
20078             cfg.cls += ' progress-striped';
20079         }
20080       
20081         if(this.active){
20082             cfg.cls += ' active';
20083         }
20084         
20085         
20086         return cfg;
20087     }
20088    
20089 });
20090
20091  
20092
20093  /*
20094  * - LGPL
20095  *
20096  * ProgressBar
20097  * 
20098  */
20099
20100 /**
20101  * @class Roo.bootstrap.ProgressBar
20102  * @extends Roo.bootstrap.Component
20103  * Bootstrap ProgressBar class
20104  * @cfg {Number} aria_valuenow aria-value now
20105  * @cfg {Number} aria_valuemin aria-value min
20106  * @cfg {Number} aria_valuemax aria-value max
20107  * @cfg {String} label label for the progress bar
20108  * @cfg {String} panel (success | info | warning | danger )
20109  * @cfg {String} role role of the progress bar
20110  * @cfg {String} sr_only text
20111  * 
20112  * 
20113  * @constructor
20114  * Create a new ProgressBar
20115  * @param {Object} config The config object
20116  */
20117
20118 Roo.bootstrap.ProgressBar = function(config){
20119     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20120 };
20121
20122 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20123     
20124     aria_valuenow : 0,
20125     aria_valuemin : 0,
20126     aria_valuemax : 100,
20127     label : false,
20128     panel : false,
20129     role : false,
20130     sr_only: false,
20131     
20132     getAutoCreate : function()
20133     {
20134         
20135         var cfg = {
20136             tag: 'div',
20137             cls: 'progress-bar',
20138             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20139         };
20140         
20141         if(this.sr_only){
20142             cfg.cn = {
20143                 tag: 'span',
20144                 cls: 'sr-only',
20145                 html: this.sr_only
20146             }
20147         }
20148         
20149         if(this.role){
20150             cfg.role = this.role;
20151         }
20152         
20153         if(this.aria_valuenow){
20154             cfg['aria-valuenow'] = this.aria_valuenow;
20155         }
20156         
20157         if(this.aria_valuemin){
20158             cfg['aria-valuemin'] = this.aria_valuemin;
20159         }
20160         
20161         if(this.aria_valuemax){
20162             cfg['aria-valuemax'] = this.aria_valuemax;
20163         }
20164         
20165         if(this.label && !this.sr_only){
20166             cfg.html = this.label;
20167         }
20168         
20169         if(this.panel){
20170             cfg.cls += ' progress-bar-' + this.panel;
20171         }
20172         
20173         return cfg;
20174     },
20175     
20176     update : function(aria_valuenow)
20177     {
20178         this.aria_valuenow = aria_valuenow;
20179         
20180         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20181     }
20182    
20183 });
20184
20185  
20186
20187  /*
20188  * - LGPL
20189  *
20190  * column
20191  * 
20192  */
20193
20194 /**
20195  * @class Roo.bootstrap.TabGroup
20196  * @extends Roo.bootstrap.Column
20197  * Bootstrap Column class
20198  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20199  * @cfg {Boolean} carousel true to make the group behave like a carousel
20200  * @cfg {Boolean} bullets show bullets for the panels
20201  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20202  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20203  * @cfg {Boolean} showarrow (true|false) show arrow default true
20204  * 
20205  * @constructor
20206  * Create a new TabGroup
20207  * @param {Object} config The config object
20208  */
20209
20210 Roo.bootstrap.TabGroup = function(config){
20211     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20212     if (!this.navId) {
20213         this.navId = Roo.id();
20214     }
20215     this.tabs = [];
20216     Roo.bootstrap.TabGroup.register(this);
20217     
20218 };
20219
20220 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20221     
20222     carousel : false,
20223     transition : false,
20224     bullets : 0,
20225     timer : 0,
20226     autoslide : false,
20227     slideFn : false,
20228     slideOnTouch : false,
20229     showarrow : true,
20230     
20231     getAutoCreate : function()
20232     {
20233         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20234         
20235         cfg.cls += ' tab-content';
20236         
20237         if (this.carousel) {
20238             cfg.cls += ' carousel slide';
20239             
20240             cfg.cn = [{
20241                cls : 'carousel-inner',
20242                cn : []
20243             }];
20244         
20245             if(this.bullets  && !Roo.isTouch){
20246                 
20247                 var bullets = {
20248                     cls : 'carousel-bullets',
20249                     cn : []
20250                 };
20251                
20252                 if(this.bullets_cls){
20253                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20254                 }
20255                 
20256                 bullets.cn.push({
20257                     cls : 'clear'
20258                 });
20259                 
20260                 cfg.cn[0].cn.push(bullets);
20261             }
20262             
20263             if(this.showarrow){
20264                 cfg.cn[0].cn.push({
20265                     tag : 'div',
20266                     class : 'carousel-arrow',
20267                     cn : [
20268                         {
20269                             tag : 'div',
20270                             class : 'carousel-prev',
20271                             cn : [
20272                                 {
20273                                     tag : 'i',
20274                                     class : 'fa fa-chevron-left'
20275                                 }
20276                             ]
20277                         },
20278                         {
20279                             tag : 'div',
20280                             class : 'carousel-next',
20281                             cn : [
20282                                 {
20283                                     tag : 'i',
20284                                     class : 'fa fa-chevron-right'
20285                                 }
20286                             ]
20287                         }
20288                     ]
20289                 });
20290             }
20291             
20292         }
20293         
20294         return cfg;
20295     },
20296     
20297     initEvents:  function()
20298     {
20299 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20300 //            this.el.on("touchstart", this.onTouchStart, this);
20301 //        }
20302         
20303         if(this.autoslide){
20304             var _this = this;
20305             
20306             this.slideFn = window.setInterval(function() {
20307                 _this.showPanelNext();
20308             }, this.timer);
20309         }
20310         
20311         if(this.showarrow){
20312             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20313             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20314         }
20315         
20316         
20317     },
20318     
20319 //    onTouchStart : function(e, el, o)
20320 //    {
20321 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20322 //            return;
20323 //        }
20324 //        
20325 //        this.showPanelNext();
20326 //    },
20327     
20328     
20329     getChildContainer : function()
20330     {
20331         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20332     },
20333     
20334     /**
20335     * register a Navigation item
20336     * @param {Roo.bootstrap.NavItem} the navitem to add
20337     */
20338     register : function(item)
20339     {
20340         this.tabs.push( item);
20341         item.navId = this.navId; // not really needed..
20342         this.addBullet();
20343     
20344     },
20345     
20346     getActivePanel : function()
20347     {
20348         var r = false;
20349         Roo.each(this.tabs, function(t) {
20350             if (t.active) {
20351                 r = t;
20352                 return false;
20353             }
20354             return null;
20355         });
20356         return r;
20357         
20358     },
20359     getPanelByName : function(n)
20360     {
20361         var r = false;
20362         Roo.each(this.tabs, function(t) {
20363             if (t.tabId == n) {
20364                 r = t;
20365                 return false;
20366             }
20367             return null;
20368         });
20369         return r;
20370     },
20371     indexOfPanel : function(p)
20372     {
20373         var r = false;
20374         Roo.each(this.tabs, function(t,i) {
20375             if (t.tabId == p.tabId) {
20376                 r = i;
20377                 return false;
20378             }
20379             return null;
20380         });
20381         return r;
20382     },
20383     /**
20384      * show a specific panel
20385      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20386      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20387      */
20388     showPanel : function (pan)
20389     {
20390         if(this.transition || typeof(pan) == 'undefined'){
20391             Roo.log("waiting for the transitionend");
20392             return false;
20393         }
20394         
20395         if (typeof(pan) == 'number') {
20396             pan = this.tabs[pan];
20397         }
20398         
20399         if (typeof(pan) == 'string') {
20400             pan = this.getPanelByName(pan);
20401         }
20402         
20403         var cur = this.getActivePanel();
20404         
20405         if(!pan || !cur){
20406             Roo.log('pan or acitve pan is undefined');
20407             return false;
20408         }
20409         
20410         if (pan.tabId == this.getActivePanel().tabId) {
20411             return true;
20412         }
20413         
20414         if (false === cur.fireEvent('beforedeactivate')) {
20415             return false;
20416         }
20417         
20418         if(this.bullets > 0 && !Roo.isTouch){
20419             this.setActiveBullet(this.indexOfPanel(pan));
20420         }
20421         
20422         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20423             
20424             //class="carousel-item carousel-item-next carousel-item-left"
20425             
20426             this.transition = true;
20427             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20428             var lr = dir == 'next' ? 'left' : 'right';
20429             pan.el.addClass(dir); // or prev
20430             pan.el.addClass('carousel-item-' + dir); // or prev
20431             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20432             cur.el.addClass(lr); // or right
20433             pan.el.addClass(lr);
20434             cur.el.addClass('carousel-item-' +lr); // or right
20435             pan.el.addClass('carousel-item-' +lr);
20436             
20437             
20438             var _this = this;
20439             cur.el.on('transitionend', function() {
20440                 Roo.log("trans end?");
20441                 
20442                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20443                 pan.setActive(true);
20444                 
20445                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20446                 cur.setActive(false);
20447                 
20448                 _this.transition = false;
20449                 
20450             }, this, { single:  true } );
20451             
20452             return true;
20453         }
20454         
20455         cur.setActive(false);
20456         pan.setActive(true);
20457         
20458         return true;
20459         
20460     },
20461     showPanelNext : function()
20462     {
20463         var i = this.indexOfPanel(this.getActivePanel());
20464         
20465         if (i >= this.tabs.length - 1 && !this.autoslide) {
20466             return;
20467         }
20468         
20469         if (i >= this.tabs.length - 1 && this.autoslide) {
20470             i = -1;
20471         }
20472         
20473         this.showPanel(this.tabs[i+1]);
20474     },
20475     
20476     showPanelPrev : function()
20477     {
20478         var i = this.indexOfPanel(this.getActivePanel());
20479         
20480         if (i  < 1 && !this.autoslide) {
20481             return;
20482         }
20483         
20484         if (i < 1 && this.autoslide) {
20485             i = this.tabs.length;
20486         }
20487         
20488         this.showPanel(this.tabs[i-1]);
20489     },
20490     
20491     
20492     addBullet: function()
20493     {
20494         if(!this.bullets || Roo.isTouch){
20495             return;
20496         }
20497         var ctr = this.el.select('.carousel-bullets',true).first();
20498         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20499         var bullet = ctr.createChild({
20500             cls : 'bullet bullet-' + i
20501         },ctr.dom.lastChild);
20502         
20503         
20504         var _this = this;
20505         
20506         bullet.on('click', (function(e, el, o, ii, t){
20507
20508             e.preventDefault();
20509
20510             this.showPanel(ii);
20511
20512             if(this.autoslide && this.slideFn){
20513                 clearInterval(this.slideFn);
20514                 this.slideFn = window.setInterval(function() {
20515                     _this.showPanelNext();
20516                 }, this.timer);
20517             }
20518
20519         }).createDelegate(this, [i, bullet], true));
20520                 
20521         
20522     },
20523      
20524     setActiveBullet : function(i)
20525     {
20526         if(Roo.isTouch){
20527             return;
20528         }
20529         
20530         Roo.each(this.el.select('.bullet', true).elements, function(el){
20531             el.removeClass('selected');
20532         });
20533
20534         var bullet = this.el.select('.bullet-' + i, true).first();
20535         
20536         if(!bullet){
20537             return;
20538         }
20539         
20540         bullet.addClass('selected');
20541     }
20542     
20543     
20544   
20545 });
20546
20547  
20548
20549  
20550  
20551 Roo.apply(Roo.bootstrap.TabGroup, {
20552     
20553     groups: {},
20554      /**
20555     * register a Navigation Group
20556     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20557     */
20558     register : function(navgrp)
20559     {
20560         this.groups[navgrp.navId] = navgrp;
20561         
20562     },
20563     /**
20564     * fetch a Navigation Group based on the navigation ID
20565     * if one does not exist , it will get created.
20566     * @param {string} the navgroup to add
20567     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20568     */
20569     get: function(navId) {
20570         if (typeof(this.groups[navId]) == 'undefined') {
20571             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20572         }
20573         return this.groups[navId] ;
20574     }
20575     
20576     
20577     
20578 });
20579
20580  /*
20581  * - LGPL
20582  *
20583  * TabPanel
20584  * 
20585  */
20586
20587 /**
20588  * @class Roo.bootstrap.TabPanel
20589  * @extends Roo.bootstrap.Component
20590  * Bootstrap TabPanel class
20591  * @cfg {Boolean} active panel active
20592  * @cfg {String} html panel content
20593  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20594  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20595  * @cfg {String} href click to link..
20596  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20597  * 
20598  * 
20599  * @constructor
20600  * Create a new TabPanel
20601  * @param {Object} config The config object
20602  */
20603
20604 Roo.bootstrap.TabPanel = function(config){
20605     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20606     this.addEvents({
20607         /**
20608              * @event changed
20609              * Fires when the active status changes
20610              * @param {Roo.bootstrap.TabPanel} this
20611              * @param {Boolean} state the new state
20612             
20613          */
20614         'changed': true,
20615         /**
20616              * @event beforedeactivate
20617              * Fires before a tab is de-activated - can be used to do validation on a form.
20618              * @param {Roo.bootstrap.TabPanel} this
20619              * @return {Boolean} false if there is an error
20620             
20621          */
20622         'beforedeactivate': true
20623      });
20624     
20625     this.tabId = this.tabId || Roo.id();
20626   
20627 };
20628
20629 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20630     
20631     active: false,
20632     html: false,
20633     tabId: false,
20634     navId : false,
20635     href : '',
20636     touchSlide : false,
20637     getAutoCreate : function(){
20638         
20639         
20640         var cfg = {
20641             tag: 'div',
20642             // item is needed for carousel - not sure if it has any effect otherwise
20643             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20644             html: this.html || ''
20645         };
20646         
20647         if(this.active){
20648             cfg.cls += ' active';
20649         }
20650         
20651         if(this.tabId){
20652             cfg.tabId = this.tabId;
20653         }
20654         
20655         
20656         
20657         return cfg;
20658     },
20659     
20660     initEvents:  function()
20661     {
20662         var p = this.parent();
20663         
20664         this.navId = this.navId || p.navId;
20665         
20666         if (typeof(this.navId) != 'undefined') {
20667             // not really needed.. but just in case.. parent should be a NavGroup.
20668             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20669             
20670             tg.register(this);
20671             
20672             var i = tg.tabs.length - 1;
20673             
20674             if(this.active && tg.bullets > 0 && i < tg.bullets){
20675                 tg.setActiveBullet(i);
20676             }
20677         }
20678         
20679         this.el.on('click', this.onClick, this);
20680         
20681         if(Roo.isTouch && this.touchSlide){
20682             this.el.on("touchstart", this.onTouchStart, this);
20683             this.el.on("touchmove", this.onTouchMove, this);
20684             this.el.on("touchend", this.onTouchEnd, this);
20685         }
20686         
20687     },
20688     
20689     onRender : function(ct, position)
20690     {
20691         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20692     },
20693     
20694     setActive : function(state)
20695     {
20696         Roo.log("panel - set active " + this.tabId + "=" + state);
20697         
20698         this.active = state;
20699         if (!state) {
20700             this.el.removeClass('active');
20701             
20702         } else  if (!this.el.hasClass('active')) {
20703             this.el.addClass('active');
20704         }
20705         
20706         this.fireEvent('changed', this, state);
20707     },
20708     
20709     onClick : function(e)
20710     {
20711         e.preventDefault();
20712         
20713         if(!this.href.length){
20714             return;
20715         }
20716         
20717         window.location.href = this.href;
20718     },
20719     
20720     startX : 0,
20721     startY : 0,
20722     endX : 0,
20723     endY : 0,
20724     swiping : false,
20725     
20726     onTouchStart : function(e)
20727     {
20728         this.swiping = false;
20729         
20730         this.startX = e.browserEvent.touches[0].clientX;
20731         this.startY = e.browserEvent.touches[0].clientY;
20732     },
20733     
20734     onTouchMove : function(e)
20735     {
20736         this.swiping = true;
20737         
20738         this.endX = e.browserEvent.touches[0].clientX;
20739         this.endY = e.browserEvent.touches[0].clientY;
20740     },
20741     
20742     onTouchEnd : function(e)
20743     {
20744         if(!this.swiping){
20745             this.onClick(e);
20746             return;
20747         }
20748         
20749         var tabGroup = this.parent();
20750         
20751         if(this.endX > this.startX){ // swiping right
20752             tabGroup.showPanelPrev();
20753             return;
20754         }
20755         
20756         if(this.startX > this.endX){ // swiping left
20757             tabGroup.showPanelNext();
20758             return;
20759         }
20760     }
20761     
20762     
20763 });
20764  
20765
20766  
20767
20768  /*
20769  * - LGPL
20770  *
20771  * DateField
20772  * 
20773  */
20774
20775 /**
20776  * @class Roo.bootstrap.DateField
20777  * @extends Roo.bootstrap.Input
20778  * Bootstrap DateField class
20779  * @cfg {Number} weekStart default 0
20780  * @cfg {String} viewMode default empty, (months|years)
20781  * @cfg {String} minViewMode default empty, (months|years)
20782  * @cfg {Number} startDate default -Infinity
20783  * @cfg {Number} endDate default Infinity
20784  * @cfg {Boolean} todayHighlight default false
20785  * @cfg {Boolean} todayBtn default false
20786  * @cfg {Boolean} calendarWeeks default false
20787  * @cfg {Object} daysOfWeekDisabled default empty
20788  * @cfg {Boolean} singleMode default false (true | false)
20789  * 
20790  * @cfg {Boolean} keyboardNavigation default true
20791  * @cfg {String} language default en
20792  * 
20793  * @constructor
20794  * Create a new DateField
20795  * @param {Object} config The config object
20796  */
20797
20798 Roo.bootstrap.DateField = function(config){
20799     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20800      this.addEvents({
20801             /**
20802              * @event show
20803              * Fires when this field show.
20804              * @param {Roo.bootstrap.DateField} this
20805              * @param {Mixed} date The date value
20806              */
20807             show : true,
20808             /**
20809              * @event show
20810              * Fires when this field hide.
20811              * @param {Roo.bootstrap.DateField} this
20812              * @param {Mixed} date The date value
20813              */
20814             hide : true,
20815             /**
20816              * @event select
20817              * Fires when select a date.
20818              * @param {Roo.bootstrap.DateField} this
20819              * @param {Mixed} date The date value
20820              */
20821             select : true,
20822             /**
20823              * @event beforeselect
20824              * Fires when before select a date.
20825              * @param {Roo.bootstrap.DateField} this
20826              * @param {Mixed} date The date value
20827              */
20828             beforeselect : true
20829         });
20830 };
20831
20832 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20833     
20834     /**
20835      * @cfg {String} format
20836      * The default date format string which can be overriden for localization support.  The format must be
20837      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20838      */
20839     format : "m/d/y",
20840     /**
20841      * @cfg {String} altFormats
20842      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20843      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20844      */
20845     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20846     
20847     weekStart : 0,
20848     
20849     viewMode : '',
20850     
20851     minViewMode : '',
20852     
20853     todayHighlight : false,
20854     
20855     todayBtn: false,
20856     
20857     language: 'en',
20858     
20859     keyboardNavigation: true,
20860     
20861     calendarWeeks: false,
20862     
20863     startDate: -Infinity,
20864     
20865     endDate: Infinity,
20866     
20867     daysOfWeekDisabled: [],
20868     
20869     _events: [],
20870     
20871     singleMode : false,
20872     
20873     UTCDate: function()
20874     {
20875         return new Date(Date.UTC.apply(Date, arguments));
20876     },
20877     
20878     UTCToday: function()
20879     {
20880         var today = new Date();
20881         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20882     },
20883     
20884     getDate: function() {
20885             var d = this.getUTCDate();
20886             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20887     },
20888     
20889     getUTCDate: function() {
20890             return this.date;
20891     },
20892     
20893     setDate: function(d) {
20894             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20895     },
20896     
20897     setUTCDate: function(d) {
20898             this.date = d;
20899             this.setValue(this.formatDate(this.date));
20900     },
20901         
20902     onRender: function(ct, position)
20903     {
20904         
20905         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20906         
20907         this.language = this.language || 'en';
20908         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20909         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20910         
20911         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20912         this.format = this.format || 'm/d/y';
20913         this.isInline = false;
20914         this.isInput = true;
20915         this.component = this.el.select('.add-on', true).first() || false;
20916         this.component = (this.component && this.component.length === 0) ? false : this.component;
20917         this.hasInput = this.component && this.inputEl().length;
20918         
20919         if (typeof(this.minViewMode === 'string')) {
20920             switch (this.minViewMode) {
20921                 case 'months':
20922                     this.minViewMode = 1;
20923                     break;
20924                 case 'years':
20925                     this.minViewMode = 2;
20926                     break;
20927                 default:
20928                     this.minViewMode = 0;
20929                     break;
20930             }
20931         }
20932         
20933         if (typeof(this.viewMode === 'string')) {
20934             switch (this.viewMode) {
20935                 case 'months':
20936                     this.viewMode = 1;
20937                     break;
20938                 case 'years':
20939                     this.viewMode = 2;
20940                     break;
20941                 default:
20942                     this.viewMode = 0;
20943                     break;
20944             }
20945         }
20946                 
20947         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20948         
20949 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20950         
20951         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20952         
20953         this.picker().on('mousedown', this.onMousedown, this);
20954         this.picker().on('click', this.onClick, this);
20955         
20956         this.picker().addClass('datepicker-dropdown');
20957         
20958         this.startViewMode = this.viewMode;
20959         
20960         if(this.singleMode){
20961             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20962                 v.setVisibilityMode(Roo.Element.DISPLAY);
20963                 v.hide();
20964             });
20965             
20966             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20967                 v.setStyle('width', '189px');
20968             });
20969         }
20970         
20971         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20972             if(!this.calendarWeeks){
20973                 v.remove();
20974                 return;
20975             }
20976             
20977             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20978             v.attr('colspan', function(i, val){
20979                 return parseInt(val) + 1;
20980             });
20981         });
20982                         
20983         
20984         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20985         
20986         this.setStartDate(this.startDate);
20987         this.setEndDate(this.endDate);
20988         
20989         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20990         
20991         this.fillDow();
20992         this.fillMonths();
20993         this.update();
20994         this.showMode();
20995         
20996         if(this.isInline) {
20997             this.showPopup();
20998         }
20999     },
21000     
21001     picker : function()
21002     {
21003         return this.pickerEl;
21004 //        return this.el.select('.datepicker', true).first();
21005     },
21006     
21007     fillDow: function()
21008     {
21009         var dowCnt = this.weekStart;
21010         
21011         var dow = {
21012             tag: 'tr',
21013             cn: [
21014                 
21015             ]
21016         };
21017         
21018         if(this.calendarWeeks){
21019             dow.cn.push({
21020                 tag: 'th',
21021                 cls: 'cw',
21022                 html: '&nbsp;'
21023             })
21024         }
21025         
21026         while (dowCnt < this.weekStart + 7) {
21027             dow.cn.push({
21028                 tag: 'th',
21029                 cls: 'dow',
21030                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21031             });
21032         }
21033         
21034         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21035     },
21036     
21037     fillMonths: function()
21038     {    
21039         var i = 0;
21040         var months = this.picker().select('>.datepicker-months td', true).first();
21041         
21042         months.dom.innerHTML = '';
21043         
21044         while (i < 12) {
21045             var month = {
21046                 tag: 'span',
21047                 cls: 'month',
21048                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21049             };
21050             
21051             months.createChild(month);
21052         }
21053         
21054     },
21055     
21056     update: function()
21057     {
21058         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;
21059         
21060         if (this.date < this.startDate) {
21061             this.viewDate = new Date(this.startDate);
21062         } else if (this.date > this.endDate) {
21063             this.viewDate = new Date(this.endDate);
21064         } else {
21065             this.viewDate = new Date(this.date);
21066         }
21067         
21068         this.fill();
21069     },
21070     
21071     fill: function() 
21072     {
21073         var d = new Date(this.viewDate),
21074                 year = d.getUTCFullYear(),
21075                 month = d.getUTCMonth(),
21076                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21077                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21078                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21079                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21080                 currentDate = this.date && this.date.valueOf(),
21081                 today = this.UTCToday();
21082         
21083         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21084         
21085 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21086         
21087 //        this.picker.select('>tfoot th.today').
21088 //                                              .text(dates[this.language].today)
21089 //                                              .toggle(this.todayBtn !== false);
21090     
21091         this.updateNavArrows();
21092         this.fillMonths();
21093                                                 
21094         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21095         
21096         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21097          
21098         prevMonth.setUTCDate(day);
21099         
21100         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21101         
21102         var nextMonth = new Date(prevMonth);
21103         
21104         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21105         
21106         nextMonth = nextMonth.valueOf();
21107         
21108         var fillMonths = false;
21109         
21110         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21111         
21112         while(prevMonth.valueOf() <= nextMonth) {
21113             var clsName = '';
21114             
21115             if (prevMonth.getUTCDay() === this.weekStart) {
21116                 if(fillMonths){
21117                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21118                 }
21119                     
21120                 fillMonths = {
21121                     tag: 'tr',
21122                     cn: []
21123                 };
21124                 
21125                 if(this.calendarWeeks){
21126                     // ISO 8601: First week contains first thursday.
21127                     // ISO also states week starts on Monday, but we can be more abstract here.
21128                     var
21129                     // Start of current week: based on weekstart/current date
21130                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21131                     // Thursday of this week
21132                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21133                     // First Thursday of year, year from thursday
21134                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21135                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21136                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21137                     
21138                     fillMonths.cn.push({
21139                         tag: 'td',
21140                         cls: 'cw',
21141                         html: calWeek
21142                     });
21143                 }
21144             }
21145             
21146             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21147                 clsName += ' old';
21148             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21149                 clsName += ' new';
21150             }
21151             if (this.todayHighlight &&
21152                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21153                 prevMonth.getUTCMonth() == today.getMonth() &&
21154                 prevMonth.getUTCDate() == today.getDate()) {
21155                 clsName += ' today';
21156             }
21157             
21158             if (currentDate && prevMonth.valueOf() === currentDate) {
21159                 clsName += ' active';
21160             }
21161             
21162             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21163                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21164                     clsName += ' disabled';
21165             }
21166             
21167             fillMonths.cn.push({
21168                 tag: 'td',
21169                 cls: 'day ' + clsName,
21170                 html: prevMonth.getDate()
21171             });
21172             
21173             prevMonth.setDate(prevMonth.getDate()+1);
21174         }
21175           
21176         var currentYear = this.date && this.date.getUTCFullYear();
21177         var currentMonth = this.date && this.date.getUTCMonth();
21178         
21179         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21180         
21181         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21182             v.removeClass('active');
21183             
21184             if(currentYear === year && k === currentMonth){
21185                 v.addClass('active');
21186             }
21187             
21188             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21189                 v.addClass('disabled');
21190             }
21191             
21192         });
21193         
21194         
21195         year = parseInt(year/10, 10) * 10;
21196         
21197         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21198         
21199         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21200         
21201         year -= 1;
21202         for (var i = -1; i < 11; i++) {
21203             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21204                 tag: 'span',
21205                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21206                 html: year
21207             });
21208             
21209             year += 1;
21210         }
21211     },
21212     
21213     showMode: function(dir) 
21214     {
21215         if (dir) {
21216             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21217         }
21218         
21219         Roo.each(this.picker().select('>div',true).elements, function(v){
21220             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21221             v.hide();
21222         });
21223         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21224     },
21225     
21226     place: function()
21227     {
21228         if(this.isInline) {
21229             return;
21230         }
21231         
21232         this.picker().removeClass(['bottom', 'top']);
21233         
21234         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21235             /*
21236              * place to the top of element!
21237              *
21238              */
21239             
21240             this.picker().addClass('top');
21241             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21242             
21243             return;
21244         }
21245         
21246         this.picker().addClass('bottom');
21247         
21248         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21249     },
21250     
21251     parseDate : function(value)
21252     {
21253         if(!value || value instanceof Date){
21254             return value;
21255         }
21256         var v = Date.parseDate(value, this.format);
21257         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21258             v = Date.parseDate(value, 'Y-m-d');
21259         }
21260         if(!v && this.altFormats){
21261             if(!this.altFormatsArray){
21262                 this.altFormatsArray = this.altFormats.split("|");
21263             }
21264             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21265                 v = Date.parseDate(value, this.altFormatsArray[i]);
21266             }
21267         }
21268         return v;
21269     },
21270     
21271     formatDate : function(date, fmt)
21272     {   
21273         return (!date || !(date instanceof Date)) ?
21274         date : date.dateFormat(fmt || this.format);
21275     },
21276     
21277     onFocus : function()
21278     {
21279         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21280         this.showPopup();
21281     },
21282     
21283     onBlur : function()
21284     {
21285         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21286         
21287         var d = this.inputEl().getValue();
21288         
21289         this.setValue(d);
21290                 
21291         this.hidePopup();
21292     },
21293     
21294     showPopup : function()
21295     {
21296         this.picker().show();
21297         this.update();
21298         this.place();
21299         
21300         this.fireEvent('showpopup', this, this.date);
21301     },
21302     
21303     hidePopup : function()
21304     {
21305         if(this.isInline) {
21306             return;
21307         }
21308         this.picker().hide();
21309         this.viewMode = this.startViewMode;
21310         this.showMode();
21311         
21312         this.fireEvent('hidepopup', this, this.date);
21313         
21314     },
21315     
21316     onMousedown: function(e)
21317     {
21318         e.stopPropagation();
21319         e.preventDefault();
21320     },
21321     
21322     keyup: function(e)
21323     {
21324         Roo.bootstrap.DateField.superclass.keyup.call(this);
21325         this.update();
21326     },
21327
21328     setValue: function(v)
21329     {
21330         if(this.fireEvent('beforeselect', this, v) !== false){
21331             var d = new Date(this.parseDate(v) ).clearTime();
21332         
21333             if(isNaN(d.getTime())){
21334                 this.date = this.viewDate = '';
21335                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21336                 return;
21337             }
21338
21339             v = this.formatDate(d);
21340
21341             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21342
21343             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21344
21345             this.update();
21346
21347             this.fireEvent('select', this, this.date);
21348         }
21349     },
21350     
21351     getValue: function()
21352     {
21353         return this.formatDate(this.date);
21354     },
21355     
21356     fireKey: function(e)
21357     {
21358         if (!this.picker().isVisible()){
21359             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21360                 this.showPopup();
21361             }
21362             return;
21363         }
21364         
21365         var dateChanged = false,
21366         dir, day, month,
21367         newDate, newViewDate;
21368         
21369         switch(e.keyCode){
21370             case 27: // escape
21371                 this.hidePopup();
21372                 e.preventDefault();
21373                 break;
21374             case 37: // left
21375             case 39: // right
21376                 if (!this.keyboardNavigation) {
21377                     break;
21378                 }
21379                 dir = e.keyCode == 37 ? -1 : 1;
21380                 
21381                 if (e.ctrlKey){
21382                     newDate = this.moveYear(this.date, dir);
21383                     newViewDate = this.moveYear(this.viewDate, dir);
21384                 } else if (e.shiftKey){
21385                     newDate = this.moveMonth(this.date, dir);
21386                     newViewDate = this.moveMonth(this.viewDate, dir);
21387                 } else {
21388                     newDate = new Date(this.date);
21389                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21390                     newViewDate = new Date(this.viewDate);
21391                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21392                 }
21393                 if (this.dateWithinRange(newDate)){
21394                     this.date = newDate;
21395                     this.viewDate = newViewDate;
21396                     this.setValue(this.formatDate(this.date));
21397 //                    this.update();
21398                     e.preventDefault();
21399                     dateChanged = true;
21400                 }
21401                 break;
21402             case 38: // up
21403             case 40: // down
21404                 if (!this.keyboardNavigation) {
21405                     break;
21406                 }
21407                 dir = e.keyCode == 38 ? -1 : 1;
21408                 if (e.ctrlKey){
21409                     newDate = this.moveYear(this.date, dir);
21410                     newViewDate = this.moveYear(this.viewDate, dir);
21411                 } else if (e.shiftKey){
21412                     newDate = this.moveMonth(this.date, dir);
21413                     newViewDate = this.moveMonth(this.viewDate, dir);
21414                 } else {
21415                     newDate = new Date(this.date);
21416                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21417                     newViewDate = new Date(this.viewDate);
21418                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21419                 }
21420                 if (this.dateWithinRange(newDate)){
21421                     this.date = newDate;
21422                     this.viewDate = newViewDate;
21423                     this.setValue(this.formatDate(this.date));
21424 //                    this.update();
21425                     e.preventDefault();
21426                     dateChanged = true;
21427                 }
21428                 break;
21429             case 13: // enter
21430                 this.setValue(this.formatDate(this.date));
21431                 this.hidePopup();
21432                 e.preventDefault();
21433                 break;
21434             case 9: // tab
21435                 this.setValue(this.formatDate(this.date));
21436                 this.hidePopup();
21437                 break;
21438             case 16: // shift
21439             case 17: // ctrl
21440             case 18: // alt
21441                 break;
21442             default :
21443                 this.hidePopup();
21444                 
21445         }
21446     },
21447     
21448     
21449     onClick: function(e) 
21450     {
21451         e.stopPropagation();
21452         e.preventDefault();
21453         
21454         var target = e.getTarget();
21455         
21456         if(target.nodeName.toLowerCase() === 'i'){
21457             target = Roo.get(target).dom.parentNode;
21458         }
21459         
21460         var nodeName = target.nodeName;
21461         var className = target.className;
21462         var html = target.innerHTML;
21463         //Roo.log(nodeName);
21464         
21465         switch(nodeName.toLowerCase()) {
21466             case 'th':
21467                 switch(className) {
21468                     case 'switch':
21469                         this.showMode(1);
21470                         break;
21471                     case 'prev':
21472                     case 'next':
21473                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21474                         switch(this.viewMode){
21475                                 case 0:
21476                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21477                                         break;
21478                                 case 1:
21479                                 case 2:
21480                                         this.viewDate = this.moveYear(this.viewDate, dir);
21481                                         break;
21482                         }
21483                         this.fill();
21484                         break;
21485                     case 'today':
21486                         var date = new Date();
21487                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21488 //                        this.fill()
21489                         this.setValue(this.formatDate(this.date));
21490                         
21491                         this.hidePopup();
21492                         break;
21493                 }
21494                 break;
21495             case 'span':
21496                 if (className.indexOf('disabled') < 0) {
21497                     this.viewDate.setUTCDate(1);
21498                     if (className.indexOf('month') > -1) {
21499                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21500                     } else {
21501                         var year = parseInt(html, 10) || 0;
21502                         this.viewDate.setUTCFullYear(year);
21503                         
21504                     }
21505                     
21506                     if(this.singleMode){
21507                         this.setValue(this.formatDate(this.viewDate));
21508                         this.hidePopup();
21509                         return;
21510                     }
21511                     
21512                     this.showMode(-1);
21513                     this.fill();
21514                 }
21515                 break;
21516                 
21517             case 'td':
21518                 //Roo.log(className);
21519                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21520                     var day = parseInt(html, 10) || 1;
21521                     var year = this.viewDate.getUTCFullYear(),
21522                         month = this.viewDate.getUTCMonth();
21523
21524                     if (className.indexOf('old') > -1) {
21525                         if(month === 0 ){
21526                             month = 11;
21527                             year -= 1;
21528                         }else{
21529                             month -= 1;
21530                         }
21531                     } else if (className.indexOf('new') > -1) {
21532                         if (month == 11) {
21533                             month = 0;
21534                             year += 1;
21535                         } else {
21536                             month += 1;
21537                         }
21538                     }
21539                     //Roo.log([year,month,day]);
21540                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21541                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21542 //                    this.fill();
21543                     //Roo.log(this.formatDate(this.date));
21544                     this.setValue(this.formatDate(this.date));
21545                     this.hidePopup();
21546                 }
21547                 break;
21548         }
21549     },
21550     
21551     setStartDate: function(startDate)
21552     {
21553         this.startDate = startDate || -Infinity;
21554         if (this.startDate !== -Infinity) {
21555             this.startDate = this.parseDate(this.startDate);
21556         }
21557         this.update();
21558         this.updateNavArrows();
21559     },
21560
21561     setEndDate: function(endDate)
21562     {
21563         this.endDate = endDate || Infinity;
21564         if (this.endDate !== Infinity) {
21565             this.endDate = this.parseDate(this.endDate);
21566         }
21567         this.update();
21568         this.updateNavArrows();
21569     },
21570     
21571     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21572     {
21573         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21574         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21575             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21576         }
21577         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21578             return parseInt(d, 10);
21579         });
21580         this.update();
21581         this.updateNavArrows();
21582     },
21583     
21584     updateNavArrows: function() 
21585     {
21586         if(this.singleMode){
21587             return;
21588         }
21589         
21590         var d = new Date(this.viewDate),
21591         year = d.getUTCFullYear(),
21592         month = d.getUTCMonth();
21593         
21594         Roo.each(this.picker().select('.prev', true).elements, function(v){
21595             v.show();
21596             switch (this.viewMode) {
21597                 case 0:
21598
21599                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21600                         v.hide();
21601                     }
21602                     break;
21603                 case 1:
21604                 case 2:
21605                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21606                         v.hide();
21607                     }
21608                     break;
21609             }
21610         });
21611         
21612         Roo.each(this.picker().select('.next', true).elements, function(v){
21613             v.show();
21614             switch (this.viewMode) {
21615                 case 0:
21616
21617                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21618                         v.hide();
21619                     }
21620                     break;
21621                 case 1:
21622                 case 2:
21623                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21624                         v.hide();
21625                     }
21626                     break;
21627             }
21628         })
21629     },
21630     
21631     moveMonth: function(date, dir)
21632     {
21633         if (!dir) {
21634             return date;
21635         }
21636         var new_date = new Date(date.valueOf()),
21637         day = new_date.getUTCDate(),
21638         month = new_date.getUTCMonth(),
21639         mag = Math.abs(dir),
21640         new_month, test;
21641         dir = dir > 0 ? 1 : -1;
21642         if (mag == 1){
21643             test = dir == -1
21644             // If going back one month, make sure month is not current month
21645             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21646             ? function(){
21647                 return new_date.getUTCMonth() == month;
21648             }
21649             // If going forward one month, make sure month is as expected
21650             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21651             : function(){
21652                 return new_date.getUTCMonth() != new_month;
21653             };
21654             new_month = month + dir;
21655             new_date.setUTCMonth(new_month);
21656             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21657             if (new_month < 0 || new_month > 11) {
21658                 new_month = (new_month + 12) % 12;
21659             }
21660         } else {
21661             // For magnitudes >1, move one month at a time...
21662             for (var i=0; i<mag; i++) {
21663                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21664                 new_date = this.moveMonth(new_date, dir);
21665             }
21666             // ...then reset the day, keeping it in the new month
21667             new_month = new_date.getUTCMonth();
21668             new_date.setUTCDate(day);
21669             test = function(){
21670                 return new_month != new_date.getUTCMonth();
21671             };
21672         }
21673         // Common date-resetting loop -- if date is beyond end of month, make it
21674         // end of month
21675         while (test()){
21676             new_date.setUTCDate(--day);
21677             new_date.setUTCMonth(new_month);
21678         }
21679         return new_date;
21680     },
21681
21682     moveYear: function(date, dir)
21683     {
21684         return this.moveMonth(date, dir*12);
21685     },
21686
21687     dateWithinRange: function(date)
21688     {
21689         return date >= this.startDate && date <= this.endDate;
21690     },
21691
21692     
21693     remove: function() 
21694     {
21695         this.picker().remove();
21696     },
21697     
21698     validateValue : function(value)
21699     {
21700         if(this.getVisibilityEl().hasClass('hidden')){
21701             return true;
21702         }
21703         
21704         if(value.length < 1)  {
21705             if(this.allowBlank){
21706                 return true;
21707             }
21708             return false;
21709         }
21710         
21711         if(value.length < this.minLength){
21712             return false;
21713         }
21714         if(value.length > this.maxLength){
21715             return false;
21716         }
21717         if(this.vtype){
21718             var vt = Roo.form.VTypes;
21719             if(!vt[this.vtype](value, this)){
21720                 return false;
21721             }
21722         }
21723         if(typeof this.validator == "function"){
21724             var msg = this.validator(value);
21725             if(msg !== true){
21726                 return false;
21727             }
21728         }
21729         
21730         if(this.regex && !this.regex.test(value)){
21731             return false;
21732         }
21733         
21734         if(typeof(this.parseDate(value)) == 'undefined'){
21735             return false;
21736         }
21737         
21738         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21739             return false;
21740         }      
21741         
21742         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21743             return false;
21744         } 
21745         
21746         
21747         return true;
21748     },
21749     
21750     reset : function()
21751     {
21752         this.date = this.viewDate = '';
21753         
21754         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21755     }
21756    
21757 });
21758
21759 Roo.apply(Roo.bootstrap.DateField,  {
21760     
21761     head : {
21762         tag: 'thead',
21763         cn: [
21764         {
21765             tag: 'tr',
21766             cn: [
21767             {
21768                 tag: 'th',
21769                 cls: 'prev',
21770                 html: '<i class="fa fa-arrow-left"/>'
21771             },
21772             {
21773                 tag: 'th',
21774                 cls: 'switch',
21775                 colspan: '5'
21776             },
21777             {
21778                 tag: 'th',
21779                 cls: 'next',
21780                 html: '<i class="fa fa-arrow-right"/>'
21781             }
21782
21783             ]
21784         }
21785         ]
21786     },
21787     
21788     content : {
21789         tag: 'tbody',
21790         cn: [
21791         {
21792             tag: 'tr',
21793             cn: [
21794             {
21795                 tag: 'td',
21796                 colspan: '7'
21797             }
21798             ]
21799         }
21800         ]
21801     },
21802     
21803     footer : {
21804         tag: 'tfoot',
21805         cn: [
21806         {
21807             tag: 'tr',
21808             cn: [
21809             {
21810                 tag: 'th',
21811                 colspan: '7',
21812                 cls: 'today'
21813             }
21814                     
21815             ]
21816         }
21817         ]
21818     },
21819     
21820     dates:{
21821         en: {
21822             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21823             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21824             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21825             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21826             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21827             today: "Today"
21828         }
21829     },
21830     
21831     modes: [
21832     {
21833         clsName: 'days',
21834         navFnc: 'Month',
21835         navStep: 1
21836     },
21837     {
21838         clsName: 'months',
21839         navFnc: 'FullYear',
21840         navStep: 1
21841     },
21842     {
21843         clsName: 'years',
21844         navFnc: 'FullYear',
21845         navStep: 10
21846     }]
21847 });
21848
21849 Roo.apply(Roo.bootstrap.DateField,  {
21850   
21851     template : {
21852         tag: 'div',
21853         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21854         cn: [
21855         {
21856             tag: 'div',
21857             cls: 'datepicker-days',
21858             cn: [
21859             {
21860                 tag: 'table',
21861                 cls: 'table-condensed',
21862                 cn:[
21863                 Roo.bootstrap.DateField.head,
21864                 {
21865                     tag: 'tbody'
21866                 },
21867                 Roo.bootstrap.DateField.footer
21868                 ]
21869             }
21870             ]
21871         },
21872         {
21873             tag: 'div',
21874             cls: 'datepicker-months',
21875             cn: [
21876             {
21877                 tag: 'table',
21878                 cls: 'table-condensed',
21879                 cn:[
21880                 Roo.bootstrap.DateField.head,
21881                 Roo.bootstrap.DateField.content,
21882                 Roo.bootstrap.DateField.footer
21883                 ]
21884             }
21885             ]
21886         },
21887         {
21888             tag: 'div',
21889             cls: 'datepicker-years',
21890             cn: [
21891             {
21892                 tag: 'table',
21893                 cls: 'table-condensed',
21894                 cn:[
21895                 Roo.bootstrap.DateField.head,
21896                 Roo.bootstrap.DateField.content,
21897                 Roo.bootstrap.DateField.footer
21898                 ]
21899             }
21900             ]
21901         }
21902         ]
21903     }
21904 });
21905
21906  
21907
21908  /*
21909  * - LGPL
21910  *
21911  * TimeField
21912  * 
21913  */
21914
21915 /**
21916  * @class Roo.bootstrap.TimeField
21917  * @extends Roo.bootstrap.Input
21918  * Bootstrap DateField class
21919  * 
21920  * 
21921  * @constructor
21922  * Create a new TimeField
21923  * @param {Object} config The config object
21924  */
21925
21926 Roo.bootstrap.TimeField = function(config){
21927     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21928     this.addEvents({
21929             /**
21930              * @event show
21931              * Fires when this field show.
21932              * @param {Roo.bootstrap.DateField} thisthis
21933              * @param {Mixed} date The date value
21934              */
21935             show : true,
21936             /**
21937              * @event show
21938              * Fires when this field hide.
21939              * @param {Roo.bootstrap.DateField} this
21940              * @param {Mixed} date The date value
21941              */
21942             hide : true,
21943             /**
21944              * @event select
21945              * Fires when select a date.
21946              * @param {Roo.bootstrap.DateField} this
21947              * @param {Mixed} date The date value
21948              */
21949             select : true
21950         });
21951 };
21952
21953 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21954     
21955     /**
21956      * @cfg {String} format
21957      * The default time format string which can be overriden for localization support.  The format must be
21958      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21959      */
21960     format : "H:i",
21961        
21962     onRender: function(ct, position)
21963     {
21964         
21965         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21966                 
21967         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21968         
21969         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21970         
21971         this.pop = this.picker().select('>.datepicker-time',true).first();
21972         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21973         
21974         this.picker().on('mousedown', this.onMousedown, this);
21975         this.picker().on('click', this.onClick, this);
21976         
21977         this.picker().addClass('datepicker-dropdown');
21978     
21979         this.fillTime();
21980         this.update();
21981             
21982         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21983         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21984         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21985         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21986         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21987         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21988
21989     },
21990     
21991     fireKey: function(e){
21992         if (!this.picker().isVisible()){
21993             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21994                 this.show();
21995             }
21996             return;
21997         }
21998
21999         e.preventDefault();
22000         
22001         switch(e.keyCode){
22002             case 27: // escape
22003                 this.hide();
22004                 break;
22005             case 37: // left
22006             case 39: // right
22007                 this.onTogglePeriod();
22008                 break;
22009             case 38: // up
22010                 this.onIncrementMinutes();
22011                 break;
22012             case 40: // down
22013                 this.onDecrementMinutes();
22014                 break;
22015             case 13: // enter
22016             case 9: // tab
22017                 this.setTime();
22018                 break;
22019         }
22020     },
22021     
22022     onClick: function(e) {
22023         e.stopPropagation();
22024         e.preventDefault();
22025     },
22026     
22027     picker : function()
22028     {
22029         return this.el.select('.datepicker', true).first();
22030     },
22031     
22032     fillTime: function()
22033     {    
22034         var time = this.pop.select('tbody', true).first();
22035         
22036         time.dom.innerHTML = '';
22037         
22038         time.createChild({
22039             tag: 'tr',
22040             cn: [
22041                 {
22042                     tag: 'td',
22043                     cn: [
22044                         {
22045                             tag: 'a',
22046                             href: '#',
22047                             cls: 'btn',
22048                             cn: [
22049                                 {
22050                                     tag: 'span',
22051                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22052                                 }
22053                             ]
22054                         } 
22055                     ]
22056                 },
22057                 {
22058                     tag: 'td',
22059                     cls: 'separator'
22060                 },
22061                 {
22062                     tag: 'td',
22063                     cn: [
22064                         {
22065                             tag: 'a',
22066                             href: '#',
22067                             cls: 'btn',
22068                             cn: [
22069                                 {
22070                                     tag: 'span',
22071                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22072                                 }
22073                             ]
22074                         }
22075                     ]
22076                 },
22077                 {
22078                     tag: 'td',
22079                     cls: 'separator'
22080                 }
22081             ]
22082         });
22083         
22084         time.createChild({
22085             tag: 'tr',
22086             cn: [
22087                 {
22088                     tag: 'td',
22089                     cn: [
22090                         {
22091                             tag: 'span',
22092                             cls: 'timepicker-hour',
22093                             html: '00'
22094                         }  
22095                     ]
22096                 },
22097                 {
22098                     tag: 'td',
22099                     cls: 'separator',
22100                     html: ':'
22101                 },
22102                 {
22103                     tag: 'td',
22104                     cn: [
22105                         {
22106                             tag: 'span',
22107                             cls: 'timepicker-minute',
22108                             html: '00'
22109                         }  
22110                     ]
22111                 },
22112                 {
22113                     tag: 'td',
22114                     cls: 'separator'
22115                 },
22116                 {
22117                     tag: 'td',
22118                     cn: [
22119                         {
22120                             tag: 'button',
22121                             type: 'button',
22122                             cls: 'btn btn-primary period',
22123                             html: 'AM'
22124                             
22125                         }
22126                     ]
22127                 }
22128             ]
22129         });
22130         
22131         time.createChild({
22132             tag: 'tr',
22133             cn: [
22134                 {
22135                     tag: 'td',
22136                     cn: [
22137                         {
22138                             tag: 'a',
22139                             href: '#',
22140                             cls: 'btn',
22141                             cn: [
22142                                 {
22143                                     tag: 'span',
22144                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22145                                 }
22146                             ]
22147                         }
22148                     ]
22149                 },
22150                 {
22151                     tag: 'td',
22152                     cls: 'separator'
22153                 },
22154                 {
22155                     tag: 'td',
22156                     cn: [
22157                         {
22158                             tag: 'a',
22159                             href: '#',
22160                             cls: 'btn',
22161                             cn: [
22162                                 {
22163                                     tag: 'span',
22164                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22165                                 }
22166                             ]
22167                         }
22168                     ]
22169                 },
22170                 {
22171                     tag: 'td',
22172                     cls: 'separator'
22173                 }
22174             ]
22175         });
22176         
22177     },
22178     
22179     update: function()
22180     {
22181         
22182         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22183         
22184         this.fill();
22185     },
22186     
22187     fill: function() 
22188     {
22189         var hours = this.time.getHours();
22190         var minutes = this.time.getMinutes();
22191         var period = 'AM';
22192         
22193         if(hours > 11){
22194             period = 'PM';
22195         }
22196         
22197         if(hours == 0){
22198             hours = 12;
22199         }
22200         
22201         
22202         if(hours > 12){
22203             hours = hours - 12;
22204         }
22205         
22206         if(hours < 10){
22207             hours = '0' + hours;
22208         }
22209         
22210         if(minutes < 10){
22211             minutes = '0' + minutes;
22212         }
22213         
22214         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22215         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22216         this.pop.select('button', true).first().dom.innerHTML = period;
22217         
22218     },
22219     
22220     place: function()
22221     {   
22222         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22223         
22224         var cls = ['bottom'];
22225         
22226         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22227             cls.pop();
22228             cls.push('top');
22229         }
22230         
22231         cls.push('right');
22232         
22233         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22234             cls.pop();
22235             cls.push('left');
22236         }
22237         
22238         this.picker().addClass(cls.join('-'));
22239         
22240         var _this = this;
22241         
22242         Roo.each(cls, function(c){
22243             if(c == 'bottom'){
22244                 _this.picker().setTop(_this.inputEl().getHeight());
22245                 return;
22246             }
22247             if(c == 'top'){
22248                 _this.picker().setTop(0 - _this.picker().getHeight());
22249                 return;
22250             }
22251             
22252             if(c == 'left'){
22253                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22254                 return;
22255             }
22256             if(c == 'right'){
22257                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22258                 return;
22259             }
22260         });
22261         
22262     },
22263   
22264     onFocus : function()
22265     {
22266         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22267         this.show();
22268     },
22269     
22270     onBlur : function()
22271     {
22272         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22273         this.hide();
22274     },
22275     
22276     show : function()
22277     {
22278         this.picker().show();
22279         this.pop.show();
22280         this.update();
22281         this.place();
22282         
22283         this.fireEvent('show', this, this.date);
22284     },
22285     
22286     hide : function()
22287     {
22288         this.picker().hide();
22289         this.pop.hide();
22290         
22291         this.fireEvent('hide', this, this.date);
22292     },
22293     
22294     setTime : function()
22295     {
22296         this.hide();
22297         this.setValue(this.time.format(this.format));
22298         
22299         this.fireEvent('select', this, this.date);
22300         
22301         
22302     },
22303     
22304     onMousedown: function(e){
22305         e.stopPropagation();
22306         e.preventDefault();
22307     },
22308     
22309     onIncrementHours: function()
22310     {
22311         Roo.log('onIncrementHours');
22312         this.time = this.time.add(Date.HOUR, 1);
22313         this.update();
22314         
22315     },
22316     
22317     onDecrementHours: function()
22318     {
22319         Roo.log('onDecrementHours');
22320         this.time = this.time.add(Date.HOUR, -1);
22321         this.update();
22322     },
22323     
22324     onIncrementMinutes: function()
22325     {
22326         Roo.log('onIncrementMinutes');
22327         this.time = this.time.add(Date.MINUTE, 1);
22328         this.update();
22329     },
22330     
22331     onDecrementMinutes: function()
22332     {
22333         Roo.log('onDecrementMinutes');
22334         this.time = this.time.add(Date.MINUTE, -1);
22335         this.update();
22336     },
22337     
22338     onTogglePeriod: function()
22339     {
22340         Roo.log('onTogglePeriod');
22341         this.time = this.time.add(Date.HOUR, 12);
22342         this.update();
22343     }
22344     
22345    
22346 });
22347
22348 Roo.apply(Roo.bootstrap.TimeField,  {
22349     
22350     content : {
22351         tag: 'tbody',
22352         cn: [
22353             {
22354                 tag: 'tr',
22355                 cn: [
22356                 {
22357                     tag: 'td',
22358                     colspan: '7'
22359                 }
22360                 ]
22361             }
22362         ]
22363     },
22364     
22365     footer : {
22366         tag: 'tfoot',
22367         cn: [
22368             {
22369                 tag: 'tr',
22370                 cn: [
22371                 {
22372                     tag: 'th',
22373                     colspan: '7',
22374                     cls: '',
22375                     cn: [
22376                         {
22377                             tag: 'button',
22378                             cls: 'btn btn-info ok',
22379                             html: 'OK'
22380                         }
22381                     ]
22382                 }
22383
22384                 ]
22385             }
22386         ]
22387     }
22388 });
22389
22390 Roo.apply(Roo.bootstrap.TimeField,  {
22391   
22392     template : {
22393         tag: 'div',
22394         cls: 'datepicker dropdown-menu',
22395         cn: [
22396             {
22397                 tag: 'div',
22398                 cls: 'datepicker-time',
22399                 cn: [
22400                 {
22401                     tag: 'table',
22402                     cls: 'table-condensed',
22403                     cn:[
22404                     Roo.bootstrap.TimeField.content,
22405                     Roo.bootstrap.TimeField.footer
22406                     ]
22407                 }
22408                 ]
22409             }
22410         ]
22411     }
22412 });
22413
22414  
22415
22416  /*
22417  * - LGPL
22418  *
22419  * MonthField
22420  * 
22421  */
22422
22423 /**
22424  * @class Roo.bootstrap.MonthField
22425  * @extends Roo.bootstrap.Input
22426  * Bootstrap MonthField class
22427  * 
22428  * @cfg {String} language default en
22429  * 
22430  * @constructor
22431  * Create a new MonthField
22432  * @param {Object} config The config object
22433  */
22434
22435 Roo.bootstrap.MonthField = function(config){
22436     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22437     
22438     this.addEvents({
22439         /**
22440          * @event show
22441          * Fires when this field show.
22442          * @param {Roo.bootstrap.MonthField} this
22443          * @param {Mixed} date The date value
22444          */
22445         show : true,
22446         /**
22447          * @event show
22448          * Fires when this field hide.
22449          * @param {Roo.bootstrap.MonthField} this
22450          * @param {Mixed} date The date value
22451          */
22452         hide : true,
22453         /**
22454          * @event select
22455          * Fires when select a date.
22456          * @param {Roo.bootstrap.MonthField} this
22457          * @param {String} oldvalue The old value
22458          * @param {String} newvalue The new value
22459          */
22460         select : true
22461     });
22462 };
22463
22464 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22465     
22466     onRender: function(ct, position)
22467     {
22468         
22469         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22470         
22471         this.language = this.language || 'en';
22472         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22473         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22474         
22475         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22476         this.isInline = false;
22477         this.isInput = true;
22478         this.component = this.el.select('.add-on', true).first() || false;
22479         this.component = (this.component && this.component.length === 0) ? false : this.component;
22480         this.hasInput = this.component && this.inputEL().length;
22481         
22482         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22483         
22484         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22485         
22486         this.picker().on('mousedown', this.onMousedown, this);
22487         this.picker().on('click', this.onClick, this);
22488         
22489         this.picker().addClass('datepicker-dropdown');
22490         
22491         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22492             v.setStyle('width', '189px');
22493         });
22494         
22495         this.fillMonths();
22496         
22497         this.update();
22498         
22499         if(this.isInline) {
22500             this.show();
22501         }
22502         
22503     },
22504     
22505     setValue: function(v, suppressEvent)
22506     {   
22507         var o = this.getValue();
22508         
22509         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22510         
22511         this.update();
22512
22513         if(suppressEvent !== true){
22514             this.fireEvent('select', this, o, v);
22515         }
22516         
22517     },
22518     
22519     getValue: function()
22520     {
22521         return this.value;
22522     },
22523     
22524     onClick: function(e) 
22525     {
22526         e.stopPropagation();
22527         e.preventDefault();
22528         
22529         var target = e.getTarget();
22530         
22531         if(target.nodeName.toLowerCase() === 'i'){
22532             target = Roo.get(target).dom.parentNode;
22533         }
22534         
22535         var nodeName = target.nodeName;
22536         var className = target.className;
22537         var html = target.innerHTML;
22538         
22539         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22540             return;
22541         }
22542         
22543         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22544         
22545         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22546         
22547         this.hide();
22548                         
22549     },
22550     
22551     picker : function()
22552     {
22553         return this.pickerEl;
22554     },
22555     
22556     fillMonths: function()
22557     {    
22558         var i = 0;
22559         var months = this.picker().select('>.datepicker-months td', true).first();
22560         
22561         months.dom.innerHTML = '';
22562         
22563         while (i < 12) {
22564             var month = {
22565                 tag: 'span',
22566                 cls: 'month',
22567                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22568             };
22569             
22570             months.createChild(month);
22571         }
22572         
22573     },
22574     
22575     update: function()
22576     {
22577         var _this = this;
22578         
22579         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22580             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22581         }
22582         
22583         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22584             e.removeClass('active');
22585             
22586             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22587                 e.addClass('active');
22588             }
22589         })
22590     },
22591     
22592     place: function()
22593     {
22594         if(this.isInline) {
22595             return;
22596         }
22597         
22598         this.picker().removeClass(['bottom', 'top']);
22599         
22600         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22601             /*
22602              * place to the top of element!
22603              *
22604              */
22605             
22606             this.picker().addClass('top');
22607             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22608             
22609             return;
22610         }
22611         
22612         this.picker().addClass('bottom');
22613         
22614         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22615     },
22616     
22617     onFocus : function()
22618     {
22619         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22620         this.show();
22621     },
22622     
22623     onBlur : function()
22624     {
22625         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22626         
22627         var d = this.inputEl().getValue();
22628         
22629         this.setValue(d);
22630                 
22631         this.hide();
22632     },
22633     
22634     show : function()
22635     {
22636         this.picker().show();
22637         this.picker().select('>.datepicker-months', true).first().show();
22638         this.update();
22639         this.place();
22640         
22641         this.fireEvent('show', this, this.date);
22642     },
22643     
22644     hide : function()
22645     {
22646         if(this.isInline) {
22647             return;
22648         }
22649         this.picker().hide();
22650         this.fireEvent('hide', this, this.date);
22651         
22652     },
22653     
22654     onMousedown: function(e)
22655     {
22656         e.stopPropagation();
22657         e.preventDefault();
22658     },
22659     
22660     keyup: function(e)
22661     {
22662         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22663         this.update();
22664     },
22665
22666     fireKey: function(e)
22667     {
22668         if (!this.picker().isVisible()){
22669             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22670                 this.show();
22671             }
22672             return;
22673         }
22674         
22675         var dir;
22676         
22677         switch(e.keyCode){
22678             case 27: // escape
22679                 this.hide();
22680                 e.preventDefault();
22681                 break;
22682             case 37: // left
22683             case 39: // right
22684                 dir = e.keyCode == 37 ? -1 : 1;
22685                 
22686                 this.vIndex = this.vIndex + dir;
22687                 
22688                 if(this.vIndex < 0){
22689                     this.vIndex = 0;
22690                 }
22691                 
22692                 if(this.vIndex > 11){
22693                     this.vIndex = 11;
22694                 }
22695                 
22696                 if(isNaN(this.vIndex)){
22697                     this.vIndex = 0;
22698                 }
22699                 
22700                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22701                 
22702                 break;
22703             case 38: // up
22704             case 40: // down
22705                 
22706                 dir = e.keyCode == 38 ? -1 : 1;
22707                 
22708                 this.vIndex = this.vIndex + dir * 4;
22709                 
22710                 if(this.vIndex < 0){
22711                     this.vIndex = 0;
22712                 }
22713                 
22714                 if(this.vIndex > 11){
22715                     this.vIndex = 11;
22716                 }
22717                 
22718                 if(isNaN(this.vIndex)){
22719                     this.vIndex = 0;
22720                 }
22721                 
22722                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22723                 break;
22724                 
22725             case 13: // enter
22726                 
22727                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22728                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22729                 }
22730                 
22731                 this.hide();
22732                 e.preventDefault();
22733                 break;
22734             case 9: // tab
22735                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22736                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22737                 }
22738                 this.hide();
22739                 break;
22740             case 16: // shift
22741             case 17: // ctrl
22742             case 18: // alt
22743                 break;
22744             default :
22745                 this.hide();
22746                 
22747         }
22748     },
22749     
22750     remove: function() 
22751     {
22752         this.picker().remove();
22753     }
22754    
22755 });
22756
22757 Roo.apply(Roo.bootstrap.MonthField,  {
22758     
22759     content : {
22760         tag: 'tbody',
22761         cn: [
22762         {
22763             tag: 'tr',
22764             cn: [
22765             {
22766                 tag: 'td',
22767                 colspan: '7'
22768             }
22769             ]
22770         }
22771         ]
22772     },
22773     
22774     dates:{
22775         en: {
22776             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22777             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22778         }
22779     }
22780 });
22781
22782 Roo.apply(Roo.bootstrap.MonthField,  {
22783   
22784     template : {
22785         tag: 'div',
22786         cls: 'datepicker dropdown-menu roo-dynamic',
22787         cn: [
22788             {
22789                 tag: 'div',
22790                 cls: 'datepicker-months',
22791                 cn: [
22792                 {
22793                     tag: 'table',
22794                     cls: 'table-condensed',
22795                     cn:[
22796                         Roo.bootstrap.DateField.content
22797                     ]
22798                 }
22799                 ]
22800             }
22801         ]
22802     }
22803 });
22804
22805  
22806
22807  
22808  /*
22809  * - LGPL
22810  *
22811  * CheckBox
22812  * 
22813  */
22814
22815 /**
22816  * @class Roo.bootstrap.CheckBox
22817  * @extends Roo.bootstrap.Input
22818  * Bootstrap CheckBox class
22819  * 
22820  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22821  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22822  * @cfg {String} boxLabel The text that appears beside the checkbox
22823  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22824  * @cfg {Boolean} checked initnal the element
22825  * @cfg {Boolean} inline inline the element (default false)
22826  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22827  * @cfg {String} tooltip label tooltip
22828  * 
22829  * @constructor
22830  * Create a new CheckBox
22831  * @param {Object} config The config object
22832  */
22833
22834 Roo.bootstrap.CheckBox = function(config){
22835     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22836    
22837     this.addEvents({
22838         /**
22839         * @event check
22840         * Fires when the element is checked or unchecked.
22841         * @param {Roo.bootstrap.CheckBox} this This input
22842         * @param {Boolean} checked The new checked value
22843         */
22844        check : true,
22845        /**
22846         * @event click
22847         * Fires when the element is click.
22848         * @param {Roo.bootstrap.CheckBox} this This input
22849         */
22850        click : true
22851     });
22852     
22853 };
22854
22855 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22856   
22857     inputType: 'checkbox',
22858     inputValue: 1,
22859     valueOff: 0,
22860     boxLabel: false,
22861     checked: false,
22862     weight : false,
22863     inline: false,
22864     tooltip : '',
22865     
22866     // checkbox success does not make any sense really.. 
22867     invalidClass : "",
22868     validClass : "",
22869     
22870     
22871     getAutoCreate : function()
22872     {
22873         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22874         
22875         var id = Roo.id();
22876         
22877         var cfg = {};
22878         
22879         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22880         
22881         if(this.inline){
22882             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22883         }
22884         
22885         var input =  {
22886             tag: 'input',
22887             id : id,
22888             type : this.inputType,
22889             value : this.inputValue,
22890             cls : 'roo-' + this.inputType, //'form-box',
22891             placeholder : this.placeholder || ''
22892             
22893         };
22894         
22895         if(this.inputType != 'radio'){
22896             var hidden =  {
22897                 tag: 'input',
22898                 type : 'hidden',
22899                 cls : 'roo-hidden-value',
22900                 value : this.checked ? this.inputValue : this.valueOff
22901             };
22902         }
22903         
22904             
22905         if (this.weight) { // Validity check?
22906             cfg.cls += " " + this.inputType + "-" + this.weight;
22907         }
22908         
22909         if (this.disabled) {
22910             input.disabled=true;
22911         }
22912         
22913         if(this.checked){
22914             input.checked = this.checked;
22915         }
22916         
22917         if (this.name) {
22918             
22919             input.name = this.name;
22920             
22921             if(this.inputType != 'radio'){
22922                 hidden.name = this.name;
22923                 input.name = '_hidden_' + this.name;
22924             }
22925         }
22926         
22927         if (this.size) {
22928             input.cls += ' input-' + this.size;
22929         }
22930         
22931         var settings=this;
22932         
22933         ['xs','sm','md','lg'].map(function(size){
22934             if (settings[size]) {
22935                 cfg.cls += ' col-' + size + '-' + settings[size];
22936             }
22937         });
22938         
22939         var inputblock = input;
22940          
22941         if (this.before || this.after) {
22942             
22943             inputblock = {
22944                 cls : 'input-group',
22945                 cn :  [] 
22946             };
22947             
22948             if (this.before) {
22949                 inputblock.cn.push({
22950                     tag :'span',
22951                     cls : 'input-group-addon',
22952                     html : this.before
22953                 });
22954             }
22955             
22956             inputblock.cn.push(input);
22957             
22958             if(this.inputType != 'radio'){
22959                 inputblock.cn.push(hidden);
22960             }
22961             
22962             if (this.after) {
22963                 inputblock.cn.push({
22964                     tag :'span',
22965                     cls : 'input-group-addon',
22966                     html : this.after
22967                 });
22968             }
22969             
22970         }
22971         var boxLabelCfg = false;
22972         
22973         if(this.boxLabel){
22974            
22975             boxLabelCfg = {
22976                 tag: 'label',
22977                 //'for': id, // box label is handled by onclick - so no for...
22978                 cls: 'box-label',
22979                 html: this.boxLabel
22980             };
22981             if(this.tooltip){
22982                 boxLabelCfg.tooltip = this.tooltip;
22983             }
22984              
22985         }
22986         
22987         
22988         if (align ==='left' && this.fieldLabel.length) {
22989 //                Roo.log("left and has label");
22990             cfg.cn = [
22991                 {
22992                     tag: 'label',
22993                     'for' :  id,
22994                     cls : 'control-label',
22995                     html : this.fieldLabel
22996                 },
22997                 {
22998                     cls : "", 
22999                     cn: [
23000                         inputblock
23001                     ]
23002                 }
23003             ];
23004             
23005             if (boxLabelCfg) {
23006                 cfg.cn[1].cn.push(boxLabelCfg);
23007             }
23008             
23009             if(this.labelWidth > 12){
23010                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23011             }
23012             
23013             if(this.labelWidth < 13 && this.labelmd == 0){
23014                 this.labelmd = this.labelWidth;
23015             }
23016             
23017             if(this.labellg > 0){
23018                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23019                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23020             }
23021             
23022             if(this.labelmd > 0){
23023                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23024                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23025             }
23026             
23027             if(this.labelsm > 0){
23028                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23029                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23030             }
23031             
23032             if(this.labelxs > 0){
23033                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23034                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23035             }
23036             
23037         } else if ( this.fieldLabel.length) {
23038 //                Roo.log(" label");
23039                 cfg.cn = [
23040                    
23041                     {
23042                         tag: this.boxLabel ? 'span' : 'label',
23043                         'for': id,
23044                         cls: 'control-label box-input-label',
23045                         //cls : 'input-group-addon',
23046                         html : this.fieldLabel
23047                     },
23048                     
23049                     inputblock
23050                     
23051                 ];
23052                 if (boxLabelCfg) {
23053                     cfg.cn.push(boxLabelCfg);
23054                 }
23055
23056         } else {
23057             
23058 //                Roo.log(" no label && no align");
23059                 cfg.cn = [  inputblock ] ;
23060                 if (boxLabelCfg) {
23061                     cfg.cn.push(boxLabelCfg);
23062                 }
23063
23064                 
23065         }
23066         
23067        
23068         
23069         if(this.inputType != 'radio'){
23070             cfg.cn.push(hidden);
23071         }
23072         
23073         return cfg;
23074         
23075     },
23076     
23077     /**
23078      * return the real input element.
23079      */
23080     inputEl: function ()
23081     {
23082         return this.el.select('input.roo-' + this.inputType,true).first();
23083     },
23084     hiddenEl: function ()
23085     {
23086         return this.el.select('input.roo-hidden-value',true).first();
23087     },
23088     
23089     labelEl: function()
23090     {
23091         return this.el.select('label.control-label',true).first();
23092     },
23093     /* depricated... */
23094     
23095     label: function()
23096     {
23097         return this.labelEl();
23098     },
23099     
23100     boxLabelEl: function()
23101     {
23102         return this.el.select('label.box-label',true).first();
23103     },
23104     
23105     initEvents : function()
23106     {
23107 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23108         
23109         this.inputEl().on('click', this.onClick,  this);
23110         
23111         if (this.boxLabel) { 
23112             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23113         }
23114         
23115         this.startValue = this.getValue();
23116         
23117         if(this.groupId){
23118             Roo.bootstrap.CheckBox.register(this);
23119         }
23120     },
23121     
23122     onClick : function(e)
23123     {   
23124         if(this.fireEvent('click', this, e) !== false){
23125             this.setChecked(!this.checked);
23126         }
23127         
23128     },
23129     
23130     setChecked : function(state,suppressEvent)
23131     {
23132         this.startValue = this.getValue();
23133
23134         if(this.inputType == 'radio'){
23135             
23136             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23137                 e.dom.checked = false;
23138             });
23139             
23140             this.inputEl().dom.checked = true;
23141             
23142             this.inputEl().dom.value = this.inputValue;
23143             
23144             if(suppressEvent !== true){
23145                 this.fireEvent('check', this, true);
23146             }
23147             
23148             this.validate();
23149             
23150             return;
23151         }
23152         
23153         this.checked = state;
23154         
23155         this.inputEl().dom.checked = state;
23156         
23157         
23158         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23159         
23160         if(suppressEvent !== true){
23161             this.fireEvent('check', this, state);
23162         }
23163         
23164         this.validate();
23165     },
23166     
23167     getValue : function()
23168     {
23169         if(this.inputType == 'radio'){
23170             return this.getGroupValue();
23171         }
23172         
23173         return this.hiddenEl().dom.value;
23174         
23175     },
23176     
23177     getGroupValue : function()
23178     {
23179         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23180             return '';
23181         }
23182         
23183         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23184     },
23185     
23186     setValue : function(v,suppressEvent)
23187     {
23188         if(this.inputType == 'radio'){
23189             this.setGroupValue(v, suppressEvent);
23190             return;
23191         }
23192         
23193         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23194         
23195         this.validate();
23196     },
23197     
23198     setGroupValue : function(v, suppressEvent)
23199     {
23200         this.startValue = this.getValue();
23201         
23202         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23203             e.dom.checked = false;
23204             
23205             if(e.dom.value == v){
23206                 e.dom.checked = true;
23207             }
23208         });
23209         
23210         if(suppressEvent !== true){
23211             this.fireEvent('check', this, true);
23212         }
23213
23214         this.validate();
23215         
23216         return;
23217     },
23218     
23219     validate : function()
23220     {
23221         if(this.getVisibilityEl().hasClass('hidden')){
23222             return true;
23223         }
23224         
23225         if(
23226                 this.disabled || 
23227                 (this.inputType == 'radio' && this.validateRadio()) ||
23228                 (this.inputType == 'checkbox' && this.validateCheckbox())
23229         ){
23230             this.markValid();
23231             return true;
23232         }
23233         
23234         this.markInvalid();
23235         return false;
23236     },
23237     
23238     validateRadio : function()
23239     {
23240         if(this.getVisibilityEl().hasClass('hidden')){
23241             return true;
23242         }
23243         
23244         if(this.allowBlank){
23245             return true;
23246         }
23247         
23248         var valid = false;
23249         
23250         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23251             if(!e.dom.checked){
23252                 return;
23253             }
23254             
23255             valid = true;
23256             
23257             return false;
23258         });
23259         
23260         return valid;
23261     },
23262     
23263     validateCheckbox : function()
23264     {
23265         if(!this.groupId){
23266             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23267             //return (this.getValue() == this.inputValue) ? true : false;
23268         }
23269         
23270         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23271         
23272         if(!group){
23273             return false;
23274         }
23275         
23276         var r = false;
23277         
23278         for(var i in group){
23279             if(group[i].el.isVisible(true)){
23280                 r = false;
23281                 break;
23282             }
23283             
23284             r = true;
23285         }
23286         
23287         for(var i in group){
23288             if(r){
23289                 break;
23290             }
23291             
23292             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23293         }
23294         
23295         return r;
23296     },
23297     
23298     /**
23299      * Mark this field as valid
23300      */
23301     markValid : function()
23302     {
23303         var _this = this;
23304         
23305         this.fireEvent('valid', this);
23306         
23307         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23308         
23309         if(this.groupId){
23310             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23311         }
23312         
23313         if(label){
23314             label.markValid();
23315         }
23316
23317         if(this.inputType == 'radio'){
23318             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23319                 var fg = e.findParent('.form-group', false, true);
23320                 if (Roo.bootstrap.version == 3) {
23321                     fg.removeClass([_this.invalidClass, _this.validClass]);
23322                     fg.addClass(_this.validClass);
23323                 } else {
23324                     fg.removeClass(['is-valid', 'is-invalid']);
23325                     fg.addClass('is-valid');
23326                 }
23327             });
23328             
23329             return;
23330         }
23331
23332         if(!this.groupId){
23333             var fg = this.el.findParent('.form-group', false, true);
23334             if (Roo.bootstrap.version == 3) {
23335                 fg.removeClass([this.invalidClass, this.validClass]);
23336                 fg.addClass(this.validClass);
23337             } else {
23338                 fg.removeClass(['is-valid', 'is-invalid']);
23339                 fg.addClass('is-valid');
23340             }
23341             return;
23342         }
23343         
23344         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23345         
23346         if(!group){
23347             return;
23348         }
23349         
23350         for(var i in group){
23351             var fg = group[i].el.findParent('.form-group', false, true);
23352             if (Roo.bootstrap.version == 3) {
23353                 fg.removeClass([this.invalidClass, this.validClass]);
23354                 fg.addClass(this.validClass);
23355             } else {
23356                 fg.removeClass(['is-valid', 'is-invalid']);
23357                 fg.addClass('is-valid');
23358             }
23359         }
23360     },
23361     
23362      /**
23363      * Mark this field as invalid
23364      * @param {String} msg The validation message
23365      */
23366     markInvalid : function(msg)
23367     {
23368         if(this.allowBlank){
23369             return;
23370         }
23371         
23372         var _this = this;
23373         
23374         this.fireEvent('invalid', this, msg);
23375         
23376         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23377         
23378         if(this.groupId){
23379             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23380         }
23381         
23382         if(label){
23383             label.markInvalid();
23384         }
23385             
23386         if(this.inputType == 'radio'){
23387             
23388             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23389                 var fg = e.findParent('.form-group', false, true);
23390                 if (Roo.bootstrap.version == 3) {
23391                     fg.removeClass([_this.invalidClass, _this.validClass]);
23392                     fg.addClass(_this.invalidClass);
23393                 } else {
23394                     fg.removeClass(['is-invalid', 'is-valid']);
23395                     fg.addClass('is-invalid');
23396                 }
23397             });
23398             
23399             return;
23400         }
23401         
23402         if(!this.groupId){
23403             var fg = this.el.findParent('.form-group', false, true);
23404             if (Roo.bootstrap.version == 3) {
23405                 fg.removeClass([_this.invalidClass, _this.validClass]);
23406                 fg.addClass(_this.invalidClass);
23407             } else {
23408                 fg.removeClass(['is-invalid', 'is-valid']);
23409                 fg.addClass('is-invalid');
23410             }
23411             return;
23412         }
23413         
23414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23415         
23416         if(!group){
23417             return;
23418         }
23419         
23420         for(var i in group){
23421             var fg = group[i].el.findParent('.form-group', false, true);
23422             if (Roo.bootstrap.version == 3) {
23423                 fg.removeClass([_this.invalidClass, _this.validClass]);
23424                 fg.addClass(_this.invalidClass);
23425             } else {
23426                 fg.removeClass(['is-invalid', 'is-valid']);
23427                 fg.addClass('is-invalid');
23428             }
23429         }
23430         
23431     },
23432     
23433     clearInvalid : function()
23434     {
23435         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23436         
23437         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23438         
23439         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23440         
23441         if (label && label.iconEl) {
23442             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23443             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23444         }
23445     },
23446     
23447     disable : function()
23448     {
23449         if(this.inputType != 'radio'){
23450             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23451             return;
23452         }
23453         
23454         var _this = this;
23455         
23456         if(this.rendered){
23457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23458                 _this.getActionEl().addClass(this.disabledClass);
23459                 e.dom.disabled = true;
23460             });
23461         }
23462         
23463         this.disabled = true;
23464         this.fireEvent("disable", this);
23465         return this;
23466     },
23467
23468     enable : function()
23469     {
23470         if(this.inputType != 'radio'){
23471             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23472             return;
23473         }
23474         
23475         var _this = this;
23476         
23477         if(this.rendered){
23478             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23479                 _this.getActionEl().removeClass(this.disabledClass);
23480                 e.dom.disabled = false;
23481             });
23482         }
23483         
23484         this.disabled = false;
23485         this.fireEvent("enable", this);
23486         return this;
23487     },
23488     
23489     setBoxLabel : function(v)
23490     {
23491         this.boxLabel = v;
23492         
23493         if(this.rendered){
23494             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23495         }
23496     }
23497
23498 });
23499
23500 Roo.apply(Roo.bootstrap.CheckBox, {
23501     
23502     groups: {},
23503     
23504      /**
23505     * register a CheckBox Group
23506     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23507     */
23508     register : function(checkbox)
23509     {
23510         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23511             this.groups[checkbox.groupId] = {};
23512         }
23513         
23514         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23515             return;
23516         }
23517         
23518         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23519         
23520     },
23521     /**
23522     * fetch a CheckBox Group based on the group ID
23523     * @param {string} the group ID
23524     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23525     */
23526     get: function(groupId) {
23527         if (typeof(this.groups[groupId]) == 'undefined') {
23528             return false;
23529         }
23530         
23531         return this.groups[groupId] ;
23532     }
23533     
23534     
23535 });
23536 /*
23537  * - LGPL
23538  *
23539  * RadioItem
23540  * 
23541  */
23542
23543 /**
23544  * @class Roo.bootstrap.Radio
23545  * @extends Roo.bootstrap.Component
23546  * Bootstrap Radio class
23547  * @cfg {String} boxLabel - the label associated
23548  * @cfg {String} value - the value of radio
23549  * 
23550  * @constructor
23551  * Create a new Radio
23552  * @param {Object} config The config object
23553  */
23554 Roo.bootstrap.Radio = function(config){
23555     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23556     
23557 };
23558
23559 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23560     
23561     boxLabel : '',
23562     
23563     value : '',
23564     
23565     getAutoCreate : function()
23566     {
23567         var cfg = {
23568             tag : 'div',
23569             cls : 'form-group radio',
23570             cn : [
23571                 {
23572                     tag : 'label',
23573                     cls : 'box-label',
23574                     html : this.boxLabel
23575                 }
23576             ]
23577         };
23578         
23579         return cfg;
23580     },
23581     
23582     initEvents : function() 
23583     {
23584         this.parent().register(this);
23585         
23586         this.el.on('click', this.onClick, this);
23587         
23588     },
23589     
23590     onClick : function(e)
23591     {
23592         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23593             this.setChecked(true);
23594         }
23595     },
23596     
23597     setChecked : function(state, suppressEvent)
23598     {
23599         this.parent().setValue(this.value, suppressEvent);
23600         
23601     },
23602     
23603     setBoxLabel : function(v)
23604     {
23605         this.boxLabel = v;
23606         
23607         if(this.rendered){
23608             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23609         }
23610     }
23611     
23612 });
23613  
23614
23615  /*
23616  * - LGPL
23617  *
23618  * Input
23619  * 
23620  */
23621
23622 /**
23623  * @class Roo.bootstrap.SecurePass
23624  * @extends Roo.bootstrap.Input
23625  * Bootstrap SecurePass class
23626  *
23627  * 
23628  * @constructor
23629  * Create a new SecurePass
23630  * @param {Object} config The config object
23631  */
23632  
23633 Roo.bootstrap.SecurePass = function (config) {
23634     // these go here, so the translation tool can replace them..
23635     this.errors = {
23636         PwdEmpty: "Please type a password, and then retype it to confirm.",
23637         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23638         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23639         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23640         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23641         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23642         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23643         TooWeak: "Your password is Too Weak."
23644     },
23645     this.meterLabel = "Password strength:";
23646     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23647     this.meterClass = [
23648         "roo-password-meter-tooweak", 
23649         "roo-password-meter-weak", 
23650         "roo-password-meter-medium", 
23651         "roo-password-meter-strong", 
23652         "roo-password-meter-grey"
23653     ];
23654     
23655     this.errors = {};
23656     
23657     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23658 }
23659
23660 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23661     /**
23662      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23663      * {
23664      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23665      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23666      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23667      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23668      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23669      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23670      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23671      * })
23672      */
23673     // private
23674     
23675     meterWidth: 300,
23676     errorMsg :'',    
23677     errors: false,
23678     imageRoot: '/',
23679     /**
23680      * @cfg {String/Object} Label for the strength meter (defaults to
23681      * 'Password strength:')
23682      */
23683     // private
23684     meterLabel: '',
23685     /**
23686      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23687      * ['Weak', 'Medium', 'Strong'])
23688      */
23689     // private    
23690     pwdStrengths: false,    
23691     // private
23692     strength: 0,
23693     // private
23694     _lastPwd: null,
23695     // private
23696     kCapitalLetter: 0,
23697     kSmallLetter: 1,
23698     kDigit: 2,
23699     kPunctuation: 3,
23700     
23701     insecure: false,
23702     // private
23703     initEvents: function ()
23704     {
23705         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23706
23707         if (this.el.is('input[type=password]') && Roo.isSafari) {
23708             this.el.on('keydown', this.SafariOnKeyDown, this);
23709         }
23710
23711         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23712     },
23713     // private
23714     onRender: function (ct, position)
23715     {
23716         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23717         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23718         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23719
23720         this.trigger.createChild({
23721                    cn: [
23722                     {
23723                     //id: 'PwdMeter',
23724                     tag: 'div',
23725                     cls: 'roo-password-meter-grey col-xs-12',
23726                     style: {
23727                         //width: 0,
23728                         //width: this.meterWidth + 'px'                                                
23729                         }
23730                     },
23731                     {                            
23732                          cls: 'roo-password-meter-text'                          
23733                     }
23734                 ]            
23735         });
23736
23737          
23738         if (this.hideTrigger) {
23739             this.trigger.setDisplayed(false);
23740         }
23741         this.setSize(this.width || '', this.height || '');
23742     },
23743     // private
23744     onDestroy: function ()
23745     {
23746         if (this.trigger) {
23747             this.trigger.removeAllListeners();
23748             this.trigger.remove();
23749         }
23750         if (this.wrap) {
23751             this.wrap.remove();
23752         }
23753         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23754     },
23755     // private
23756     checkStrength: function ()
23757     {
23758         var pwd = this.inputEl().getValue();
23759         if (pwd == this._lastPwd) {
23760             return;
23761         }
23762
23763         var strength;
23764         if (this.ClientSideStrongPassword(pwd)) {
23765             strength = 3;
23766         } else if (this.ClientSideMediumPassword(pwd)) {
23767             strength = 2;
23768         } else if (this.ClientSideWeakPassword(pwd)) {
23769             strength = 1;
23770         } else {
23771             strength = 0;
23772         }
23773         
23774         Roo.log('strength1: ' + strength);
23775         
23776         //var pm = this.trigger.child('div/div/div').dom;
23777         var pm = this.trigger.child('div/div');
23778         pm.removeClass(this.meterClass);
23779         pm.addClass(this.meterClass[strength]);
23780                 
23781         
23782         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23783                 
23784         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23785         
23786         this._lastPwd = pwd;
23787     },
23788     reset: function ()
23789     {
23790         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23791         
23792         this._lastPwd = '';
23793         
23794         var pm = this.trigger.child('div/div');
23795         pm.removeClass(this.meterClass);
23796         pm.addClass('roo-password-meter-grey');        
23797         
23798         
23799         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23800         
23801         pt.innerHTML = '';
23802         this.inputEl().dom.type='password';
23803     },
23804     // private
23805     validateValue: function (value)
23806     {
23807         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23808             return false;
23809         }
23810         if (value.length == 0) {
23811             if (this.allowBlank) {
23812                 this.clearInvalid();
23813                 return true;
23814             }
23815
23816             this.markInvalid(this.errors.PwdEmpty);
23817             this.errorMsg = this.errors.PwdEmpty;
23818             return false;
23819         }
23820         
23821         if(this.insecure){
23822             return true;
23823         }
23824         
23825         if (!value.match(/[\x21-\x7e]+/)) {
23826             this.markInvalid(this.errors.PwdBadChar);
23827             this.errorMsg = this.errors.PwdBadChar;
23828             return false;
23829         }
23830         if (value.length < 6) {
23831             this.markInvalid(this.errors.PwdShort);
23832             this.errorMsg = this.errors.PwdShort;
23833             return false;
23834         }
23835         if (value.length > 16) {
23836             this.markInvalid(this.errors.PwdLong);
23837             this.errorMsg = this.errors.PwdLong;
23838             return false;
23839         }
23840         var strength;
23841         if (this.ClientSideStrongPassword(value)) {
23842             strength = 3;
23843         } else if (this.ClientSideMediumPassword(value)) {
23844             strength = 2;
23845         } else if (this.ClientSideWeakPassword(value)) {
23846             strength = 1;
23847         } else {
23848             strength = 0;
23849         }
23850
23851         
23852         if (strength < 2) {
23853             //this.markInvalid(this.errors.TooWeak);
23854             this.errorMsg = this.errors.TooWeak;
23855             //return false;
23856         }
23857         
23858         
23859         console.log('strength2: ' + strength);
23860         
23861         //var pm = this.trigger.child('div/div/div').dom;
23862         
23863         var pm = this.trigger.child('div/div');
23864         pm.removeClass(this.meterClass);
23865         pm.addClass(this.meterClass[strength]);
23866                 
23867         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23868                 
23869         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23870         
23871         this.errorMsg = ''; 
23872         return true;
23873     },
23874     // private
23875     CharacterSetChecks: function (type)
23876     {
23877         this.type = type;
23878         this.fResult = false;
23879     },
23880     // private
23881     isctype: function (character, type)
23882     {
23883         switch (type) {  
23884             case this.kCapitalLetter:
23885                 if (character >= 'A' && character <= 'Z') {
23886                     return true;
23887                 }
23888                 break;
23889             
23890             case this.kSmallLetter:
23891                 if (character >= 'a' && character <= 'z') {
23892                     return true;
23893                 }
23894                 break;
23895             
23896             case this.kDigit:
23897                 if (character >= '0' && character <= '9') {
23898                     return true;
23899                 }
23900                 break;
23901             
23902             case this.kPunctuation:
23903                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23904                     return true;
23905                 }
23906                 break;
23907             
23908             default:
23909                 return false;
23910         }
23911
23912     },
23913     // private
23914     IsLongEnough: function (pwd, size)
23915     {
23916         return !(pwd == null || isNaN(size) || pwd.length < size);
23917     },
23918     // private
23919     SpansEnoughCharacterSets: function (word, nb)
23920     {
23921         if (!this.IsLongEnough(word, nb))
23922         {
23923             return false;
23924         }
23925
23926         var characterSetChecks = new Array(
23927             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23928             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23929         );
23930         
23931         for (var index = 0; index < word.length; ++index) {
23932             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23933                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23934                     characterSetChecks[nCharSet].fResult = true;
23935                     break;
23936                 }
23937             }
23938         }
23939
23940         var nCharSets = 0;
23941         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23942             if (characterSetChecks[nCharSet].fResult) {
23943                 ++nCharSets;
23944             }
23945         }
23946
23947         if (nCharSets < nb) {
23948             return false;
23949         }
23950         return true;
23951     },
23952     // private
23953     ClientSideStrongPassword: function (pwd)
23954     {
23955         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23956     },
23957     // private
23958     ClientSideMediumPassword: function (pwd)
23959     {
23960         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23961     },
23962     // private
23963     ClientSideWeakPassword: function (pwd)
23964     {
23965         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23966     }
23967           
23968 })//<script type="text/javascript">
23969
23970 /*
23971  * Based  Ext JS Library 1.1.1
23972  * Copyright(c) 2006-2007, Ext JS, LLC.
23973  * LGPL
23974  *
23975  */
23976  
23977 /**
23978  * @class Roo.HtmlEditorCore
23979  * @extends Roo.Component
23980  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23981  *
23982  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23983  */
23984
23985 Roo.HtmlEditorCore = function(config){
23986     
23987     
23988     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23989     
23990     
23991     this.addEvents({
23992         /**
23993          * @event initialize
23994          * Fires when the editor is fully initialized (including the iframe)
23995          * @param {Roo.HtmlEditorCore} this
23996          */
23997         initialize: true,
23998         /**
23999          * @event activate
24000          * Fires when the editor is first receives the focus. Any insertion must wait
24001          * until after this event.
24002          * @param {Roo.HtmlEditorCore} this
24003          */
24004         activate: true,
24005          /**
24006          * @event beforesync
24007          * Fires before the textarea is updated with content from the editor iframe. Return false
24008          * to cancel the sync.
24009          * @param {Roo.HtmlEditorCore} this
24010          * @param {String} html
24011          */
24012         beforesync: true,
24013          /**
24014          * @event beforepush
24015          * Fires before the iframe editor is updated with content from the textarea. Return false
24016          * to cancel the push.
24017          * @param {Roo.HtmlEditorCore} this
24018          * @param {String} html
24019          */
24020         beforepush: true,
24021          /**
24022          * @event sync
24023          * Fires when the textarea is updated with content from the editor iframe.
24024          * @param {Roo.HtmlEditorCore} this
24025          * @param {String} html
24026          */
24027         sync: true,
24028          /**
24029          * @event push
24030          * Fires when the iframe editor is updated with content from the textarea.
24031          * @param {Roo.HtmlEditorCore} this
24032          * @param {String} html
24033          */
24034         push: true,
24035         
24036         /**
24037          * @event editorevent
24038          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24039          * @param {Roo.HtmlEditorCore} this
24040          */
24041         editorevent: true
24042         
24043     });
24044     
24045     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24046     
24047     // defaults : white / black...
24048     this.applyBlacklists();
24049     
24050     
24051     
24052 };
24053
24054
24055 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24056
24057
24058      /**
24059      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24060      */
24061     
24062     owner : false,
24063     
24064      /**
24065      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24066      *                        Roo.resizable.
24067      */
24068     resizable : false,
24069      /**
24070      * @cfg {Number} height (in pixels)
24071      */   
24072     height: 300,
24073    /**
24074      * @cfg {Number} width (in pixels)
24075      */   
24076     width: 500,
24077     
24078     /**
24079      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24080      * 
24081      */
24082     stylesheets: false,
24083     
24084     // id of frame..
24085     frameId: false,
24086     
24087     // private properties
24088     validationEvent : false,
24089     deferHeight: true,
24090     initialized : false,
24091     activated : false,
24092     sourceEditMode : false,
24093     onFocus : Roo.emptyFn,
24094     iframePad:3,
24095     hideMode:'offsets',
24096     
24097     clearUp: true,
24098     
24099     // blacklist + whitelisted elements..
24100     black: false,
24101     white: false,
24102      
24103     bodyCls : '',
24104
24105     /**
24106      * Protected method that will not generally be called directly. It
24107      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24108      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24109      */
24110     getDocMarkup : function(){
24111         // body styles..
24112         var st = '';
24113         
24114         // inherit styels from page...?? 
24115         if (this.stylesheets === false) {
24116             
24117             Roo.get(document.head).select('style').each(function(node) {
24118                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24119             });
24120             
24121             Roo.get(document.head).select('link').each(function(node) { 
24122                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24123             });
24124             
24125         } else if (!this.stylesheets.length) {
24126                 // simple..
24127                 st = '<style type="text/css">' +
24128                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24129                    '</style>';
24130         } else {
24131             for (var i in this.stylesheets) { 
24132                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24133             }
24134             
24135         }
24136         
24137         st +=  '<style type="text/css">' +
24138             'IMG { cursor: pointer } ' +
24139         '</style>';
24140
24141         var cls = 'roo-htmleditor-body';
24142         
24143         if(this.bodyCls.length){
24144             cls += ' ' + this.bodyCls;
24145         }
24146         
24147         return '<html><head>' + st  +
24148             //<style type="text/css">' +
24149             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24150             //'</style>' +
24151             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24152     },
24153
24154     // private
24155     onRender : function(ct, position)
24156     {
24157         var _t = this;
24158         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24159         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24160         
24161         
24162         this.el.dom.style.border = '0 none';
24163         this.el.dom.setAttribute('tabIndex', -1);
24164         this.el.addClass('x-hidden hide');
24165         
24166         
24167         
24168         if(Roo.isIE){ // fix IE 1px bogus margin
24169             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24170         }
24171        
24172         
24173         this.frameId = Roo.id();
24174         
24175          
24176         
24177         var iframe = this.owner.wrap.createChild({
24178             tag: 'iframe',
24179             cls: 'form-control', // bootstrap..
24180             id: this.frameId,
24181             name: this.frameId,
24182             frameBorder : 'no',
24183             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24184         }, this.el
24185         );
24186         
24187         
24188         this.iframe = iframe.dom;
24189
24190          this.assignDocWin();
24191         
24192         this.doc.designMode = 'on';
24193        
24194         this.doc.open();
24195         this.doc.write(this.getDocMarkup());
24196         this.doc.close();
24197
24198         
24199         var task = { // must defer to wait for browser to be ready
24200             run : function(){
24201                 //console.log("run task?" + this.doc.readyState);
24202                 this.assignDocWin();
24203                 if(this.doc.body || this.doc.readyState == 'complete'){
24204                     try {
24205                         this.doc.designMode="on";
24206                     } catch (e) {
24207                         return;
24208                     }
24209                     Roo.TaskMgr.stop(task);
24210                     this.initEditor.defer(10, this);
24211                 }
24212             },
24213             interval : 10,
24214             duration: 10000,
24215             scope: this
24216         };
24217         Roo.TaskMgr.start(task);
24218
24219     },
24220
24221     // private
24222     onResize : function(w, h)
24223     {
24224          Roo.log('resize: ' +w + ',' + h );
24225         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24226         if(!this.iframe){
24227             return;
24228         }
24229         if(typeof w == 'number'){
24230             
24231             this.iframe.style.width = w + 'px';
24232         }
24233         if(typeof h == 'number'){
24234             
24235             this.iframe.style.height = h + 'px';
24236             if(this.doc){
24237                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24238             }
24239         }
24240         
24241     },
24242
24243     /**
24244      * Toggles the editor between standard and source edit mode.
24245      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24246      */
24247     toggleSourceEdit : function(sourceEditMode){
24248         
24249         this.sourceEditMode = sourceEditMode === true;
24250         
24251         if(this.sourceEditMode){
24252  
24253             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24254             
24255         }else{
24256             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24257             //this.iframe.className = '';
24258             this.deferFocus();
24259         }
24260         //this.setSize(this.owner.wrap.getSize());
24261         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24262     },
24263
24264     
24265   
24266
24267     /**
24268      * Protected method that will not generally be called directly. If you need/want
24269      * custom HTML cleanup, this is the method you should override.
24270      * @param {String} html The HTML to be cleaned
24271      * return {String} The cleaned HTML
24272      */
24273     cleanHtml : function(html){
24274         html = String(html);
24275         if(html.length > 5){
24276             if(Roo.isSafari){ // strip safari nonsense
24277                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24278             }
24279         }
24280         if(html == '&nbsp;'){
24281             html = '';
24282         }
24283         return html;
24284     },
24285
24286     /**
24287      * HTML Editor -> Textarea
24288      * Protected method that will not generally be called directly. Syncs the contents
24289      * of the editor iframe with the textarea.
24290      */
24291     syncValue : function(){
24292         if(this.initialized){
24293             var bd = (this.doc.body || this.doc.documentElement);
24294             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24295             var html = bd.innerHTML;
24296             if(Roo.isSafari){
24297                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24298                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24299                 if(m && m[1]){
24300                     html = '<div style="'+m[0]+'">' + html + '</div>';
24301                 }
24302             }
24303             html = this.cleanHtml(html);
24304             // fix up the special chars.. normaly like back quotes in word...
24305             // however we do not want to do this with chinese..
24306             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24307                 
24308                 var cc = match.charCodeAt();
24309
24310                 // Get the character value, handling surrogate pairs
24311                 if (match.length == 2) {
24312                     // It's a surrogate pair, calculate the Unicode code point
24313                     var high = match.charCodeAt(0) - 0xD800;
24314                     var low  = match.charCodeAt(1) - 0xDC00;
24315                     cc = (high * 0x400) + low + 0x10000;
24316                 }  else if (
24317                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24318                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24319                     (cc >= 0xf900 && cc < 0xfb00 )
24320                 ) {
24321                         return match;
24322                 }  
24323          
24324                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24325                 return "&#" + cc + ";";
24326                 
24327                 
24328             });
24329             
24330             
24331              
24332             if(this.owner.fireEvent('beforesync', this, html) !== false){
24333                 this.el.dom.value = html;
24334                 this.owner.fireEvent('sync', this, html);
24335             }
24336         }
24337     },
24338
24339     /**
24340      * Protected method that will not generally be called directly. Pushes the value of the textarea
24341      * into the iframe editor.
24342      */
24343     pushValue : function(){
24344         if(this.initialized){
24345             var v = this.el.dom.value.trim();
24346             
24347 //            if(v.length < 1){
24348 //                v = '&#160;';
24349 //            }
24350             
24351             if(this.owner.fireEvent('beforepush', this, v) !== false){
24352                 var d = (this.doc.body || this.doc.documentElement);
24353                 d.innerHTML = v;
24354                 this.cleanUpPaste();
24355                 this.el.dom.value = d.innerHTML;
24356                 this.owner.fireEvent('push', this, v);
24357             }
24358         }
24359     },
24360
24361     // private
24362     deferFocus : function(){
24363         this.focus.defer(10, this);
24364     },
24365
24366     // doc'ed in Field
24367     focus : function(){
24368         if(this.win && !this.sourceEditMode){
24369             this.win.focus();
24370         }else{
24371             this.el.focus();
24372         }
24373     },
24374     
24375     assignDocWin: function()
24376     {
24377         var iframe = this.iframe;
24378         
24379          if(Roo.isIE){
24380             this.doc = iframe.contentWindow.document;
24381             this.win = iframe.contentWindow;
24382         } else {
24383 //            if (!Roo.get(this.frameId)) {
24384 //                return;
24385 //            }
24386 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24387 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24388             
24389             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24390                 return;
24391             }
24392             
24393             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24394             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24395         }
24396     },
24397     
24398     // private
24399     initEditor : function(){
24400         //console.log("INIT EDITOR");
24401         this.assignDocWin();
24402         
24403         
24404         
24405         this.doc.designMode="on";
24406         this.doc.open();
24407         this.doc.write(this.getDocMarkup());
24408         this.doc.close();
24409         
24410         var dbody = (this.doc.body || this.doc.documentElement);
24411         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24412         // this copies styles from the containing element into thsi one..
24413         // not sure why we need all of this..
24414         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24415         
24416         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24417         //ss['background-attachment'] = 'fixed'; // w3c
24418         dbody.bgProperties = 'fixed'; // ie
24419         //Roo.DomHelper.applyStyles(dbody, ss);
24420         Roo.EventManager.on(this.doc, {
24421             //'mousedown': this.onEditorEvent,
24422             'mouseup': this.onEditorEvent,
24423             'dblclick': this.onEditorEvent,
24424             'click': this.onEditorEvent,
24425             'keyup': this.onEditorEvent,
24426             buffer:100,
24427             scope: this
24428         });
24429         if(Roo.isGecko){
24430             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24431         }
24432         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24433             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24434         }
24435         this.initialized = true;
24436
24437         this.owner.fireEvent('initialize', this);
24438         this.pushValue();
24439     },
24440
24441     // private
24442     onDestroy : function(){
24443         
24444         
24445         
24446         if(this.rendered){
24447             
24448             //for (var i =0; i < this.toolbars.length;i++) {
24449             //    // fixme - ask toolbars for heights?
24450             //    this.toolbars[i].onDestroy();
24451            // }
24452             
24453             //this.wrap.dom.innerHTML = '';
24454             //this.wrap.remove();
24455         }
24456     },
24457
24458     // private
24459     onFirstFocus : function(){
24460         
24461         this.assignDocWin();
24462         
24463         
24464         this.activated = true;
24465          
24466     
24467         if(Roo.isGecko){ // prevent silly gecko errors
24468             this.win.focus();
24469             var s = this.win.getSelection();
24470             if(!s.focusNode || s.focusNode.nodeType != 3){
24471                 var r = s.getRangeAt(0);
24472                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24473                 r.collapse(true);
24474                 this.deferFocus();
24475             }
24476             try{
24477                 this.execCmd('useCSS', true);
24478                 this.execCmd('styleWithCSS', false);
24479             }catch(e){}
24480         }
24481         this.owner.fireEvent('activate', this);
24482     },
24483
24484     // private
24485     adjustFont: function(btn){
24486         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24487         //if(Roo.isSafari){ // safari
24488         //    adjust *= 2;
24489        // }
24490         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24491         if(Roo.isSafari){ // safari
24492             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24493             v =  (v < 10) ? 10 : v;
24494             v =  (v > 48) ? 48 : v;
24495             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24496             
24497         }
24498         
24499         
24500         v = Math.max(1, v+adjust);
24501         
24502         this.execCmd('FontSize', v  );
24503     },
24504
24505     onEditorEvent : function(e)
24506     {
24507         this.owner.fireEvent('editorevent', this, e);
24508       //  this.updateToolbar();
24509         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24510     },
24511
24512     insertTag : function(tg)
24513     {
24514         // could be a bit smarter... -> wrap the current selected tRoo..
24515         if (tg.toLowerCase() == 'span' ||
24516             tg.toLowerCase() == 'code' ||
24517             tg.toLowerCase() == 'sup' ||
24518             tg.toLowerCase() == 'sub' 
24519             ) {
24520             
24521             range = this.createRange(this.getSelection());
24522             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24523             wrappingNode.appendChild(range.extractContents());
24524             range.insertNode(wrappingNode);
24525
24526             return;
24527             
24528             
24529             
24530         }
24531         this.execCmd("formatblock",   tg);
24532         
24533     },
24534     
24535     insertText : function(txt)
24536     {
24537         
24538         
24539         var range = this.createRange();
24540         range.deleteContents();
24541                //alert(Sender.getAttribute('label'));
24542                
24543         range.insertNode(this.doc.createTextNode(txt));
24544     } ,
24545     
24546      
24547
24548     /**
24549      * Executes a Midas editor command on the editor document and performs necessary focus and
24550      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24551      * @param {String} cmd The Midas command
24552      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24553      */
24554     relayCmd : function(cmd, value){
24555         this.win.focus();
24556         this.execCmd(cmd, value);
24557         this.owner.fireEvent('editorevent', this);
24558         //this.updateToolbar();
24559         this.owner.deferFocus();
24560     },
24561
24562     /**
24563      * Executes a Midas editor command directly on the editor document.
24564      * For visual commands, you should use {@link #relayCmd} instead.
24565      * <b>This should only be called after the editor is initialized.</b>
24566      * @param {String} cmd The Midas command
24567      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24568      */
24569     execCmd : function(cmd, value){
24570         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24571         this.syncValue();
24572     },
24573  
24574  
24575    
24576     /**
24577      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24578      * to insert tRoo.
24579      * @param {String} text | dom node.. 
24580      */
24581     insertAtCursor : function(text)
24582     {
24583         
24584         if(!this.activated){
24585             return;
24586         }
24587         /*
24588         if(Roo.isIE){
24589             this.win.focus();
24590             var r = this.doc.selection.createRange();
24591             if(r){
24592                 r.collapse(true);
24593                 r.pasteHTML(text);
24594                 this.syncValue();
24595                 this.deferFocus();
24596             
24597             }
24598             return;
24599         }
24600         */
24601         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24602             this.win.focus();
24603             
24604             
24605             // from jquery ui (MIT licenced)
24606             var range, node;
24607             var win = this.win;
24608             
24609             if (win.getSelection && win.getSelection().getRangeAt) {
24610                 range = win.getSelection().getRangeAt(0);
24611                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24612                 range.insertNode(node);
24613             } else if (win.document.selection && win.document.selection.createRange) {
24614                 // no firefox support
24615                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24616                 win.document.selection.createRange().pasteHTML(txt);
24617             } else {
24618                 // no firefox support
24619                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24620                 this.execCmd('InsertHTML', txt);
24621             } 
24622             
24623             this.syncValue();
24624             
24625             this.deferFocus();
24626         }
24627     },
24628  // private
24629     mozKeyPress : function(e){
24630         if(e.ctrlKey){
24631             var c = e.getCharCode(), cmd;
24632           
24633             if(c > 0){
24634                 c = String.fromCharCode(c).toLowerCase();
24635                 switch(c){
24636                     case 'b':
24637                         cmd = 'bold';
24638                         break;
24639                     case 'i':
24640                         cmd = 'italic';
24641                         break;
24642                     
24643                     case 'u':
24644                         cmd = 'underline';
24645                         break;
24646                     
24647                     case 'v':
24648                         this.cleanUpPaste.defer(100, this);
24649                         return;
24650                         
24651                 }
24652                 if(cmd){
24653                     this.win.focus();
24654                     this.execCmd(cmd);
24655                     this.deferFocus();
24656                     e.preventDefault();
24657                 }
24658                 
24659             }
24660         }
24661     },
24662
24663     // private
24664     fixKeys : function(){ // load time branching for fastest keydown performance
24665         if(Roo.isIE){
24666             return function(e){
24667                 var k = e.getKey(), r;
24668                 if(k == e.TAB){
24669                     e.stopEvent();
24670                     r = this.doc.selection.createRange();
24671                     if(r){
24672                         r.collapse(true);
24673                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24674                         this.deferFocus();
24675                     }
24676                     return;
24677                 }
24678                 
24679                 if(k == e.ENTER){
24680                     r = this.doc.selection.createRange();
24681                     if(r){
24682                         var target = r.parentElement();
24683                         if(!target || target.tagName.toLowerCase() != 'li'){
24684                             e.stopEvent();
24685                             r.pasteHTML('<br />');
24686                             r.collapse(false);
24687                             r.select();
24688                         }
24689                     }
24690                 }
24691                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24692                     this.cleanUpPaste.defer(100, this);
24693                     return;
24694                 }
24695                 
24696                 
24697             };
24698         }else if(Roo.isOpera){
24699             return function(e){
24700                 var k = e.getKey();
24701                 if(k == e.TAB){
24702                     e.stopEvent();
24703                     this.win.focus();
24704                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24705                     this.deferFocus();
24706                 }
24707                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24708                     this.cleanUpPaste.defer(100, this);
24709                     return;
24710                 }
24711                 
24712             };
24713         }else if(Roo.isSafari){
24714             return function(e){
24715                 var k = e.getKey();
24716                 
24717                 if(k == e.TAB){
24718                     e.stopEvent();
24719                     this.execCmd('InsertText','\t');
24720                     this.deferFocus();
24721                     return;
24722                 }
24723                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24724                     this.cleanUpPaste.defer(100, this);
24725                     return;
24726                 }
24727                 
24728              };
24729         }
24730     }(),
24731     
24732     getAllAncestors: function()
24733     {
24734         var p = this.getSelectedNode();
24735         var a = [];
24736         if (!p) {
24737             a.push(p); // push blank onto stack..
24738             p = this.getParentElement();
24739         }
24740         
24741         
24742         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24743             a.push(p);
24744             p = p.parentNode;
24745         }
24746         a.push(this.doc.body);
24747         return a;
24748     },
24749     lastSel : false,
24750     lastSelNode : false,
24751     
24752     
24753     getSelection : function() 
24754     {
24755         this.assignDocWin();
24756         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24757     },
24758     
24759     getSelectedNode: function() 
24760     {
24761         // this may only work on Gecko!!!
24762         
24763         // should we cache this!!!!
24764         
24765         
24766         
24767          
24768         var range = this.createRange(this.getSelection()).cloneRange();
24769         
24770         if (Roo.isIE) {
24771             var parent = range.parentElement();
24772             while (true) {
24773                 var testRange = range.duplicate();
24774                 testRange.moveToElementText(parent);
24775                 if (testRange.inRange(range)) {
24776                     break;
24777                 }
24778                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24779                     break;
24780                 }
24781                 parent = parent.parentElement;
24782             }
24783             return parent;
24784         }
24785         
24786         // is ancestor a text element.
24787         var ac =  range.commonAncestorContainer;
24788         if (ac.nodeType == 3) {
24789             ac = ac.parentNode;
24790         }
24791         
24792         var ar = ac.childNodes;
24793          
24794         var nodes = [];
24795         var other_nodes = [];
24796         var has_other_nodes = false;
24797         for (var i=0;i<ar.length;i++) {
24798             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24799                 continue;
24800             }
24801             // fullly contained node.
24802             
24803             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24804                 nodes.push(ar[i]);
24805                 continue;
24806             }
24807             
24808             // probably selected..
24809             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24810                 other_nodes.push(ar[i]);
24811                 continue;
24812             }
24813             // outer..
24814             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24815                 continue;
24816             }
24817             
24818             
24819             has_other_nodes = true;
24820         }
24821         if (!nodes.length && other_nodes.length) {
24822             nodes= other_nodes;
24823         }
24824         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24825             return false;
24826         }
24827         
24828         return nodes[0];
24829     },
24830     createRange: function(sel)
24831     {
24832         // this has strange effects when using with 
24833         // top toolbar - not sure if it's a great idea.
24834         //this.editor.contentWindow.focus();
24835         if (typeof sel != "undefined") {
24836             try {
24837                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24838             } catch(e) {
24839                 return this.doc.createRange();
24840             }
24841         } else {
24842             return this.doc.createRange();
24843         }
24844     },
24845     getParentElement: function()
24846     {
24847         
24848         this.assignDocWin();
24849         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24850         
24851         var range = this.createRange(sel);
24852          
24853         try {
24854             var p = range.commonAncestorContainer;
24855             while (p.nodeType == 3) { // text node
24856                 p = p.parentNode;
24857             }
24858             return p;
24859         } catch (e) {
24860             return null;
24861         }
24862     
24863     },
24864     /***
24865      *
24866      * Range intersection.. the hard stuff...
24867      *  '-1' = before
24868      *  '0' = hits..
24869      *  '1' = after.
24870      *         [ -- selected range --- ]
24871      *   [fail]                        [fail]
24872      *
24873      *    basically..
24874      *      if end is before start or  hits it. fail.
24875      *      if start is after end or hits it fail.
24876      *
24877      *   if either hits (but other is outside. - then it's not 
24878      *   
24879      *    
24880      **/
24881     
24882     
24883     // @see http://www.thismuchiknow.co.uk/?p=64.
24884     rangeIntersectsNode : function(range, node)
24885     {
24886         var nodeRange = node.ownerDocument.createRange();
24887         try {
24888             nodeRange.selectNode(node);
24889         } catch (e) {
24890             nodeRange.selectNodeContents(node);
24891         }
24892     
24893         var rangeStartRange = range.cloneRange();
24894         rangeStartRange.collapse(true);
24895     
24896         var rangeEndRange = range.cloneRange();
24897         rangeEndRange.collapse(false);
24898     
24899         var nodeStartRange = nodeRange.cloneRange();
24900         nodeStartRange.collapse(true);
24901     
24902         var nodeEndRange = nodeRange.cloneRange();
24903         nodeEndRange.collapse(false);
24904     
24905         return rangeStartRange.compareBoundaryPoints(
24906                  Range.START_TO_START, nodeEndRange) == -1 &&
24907                rangeEndRange.compareBoundaryPoints(
24908                  Range.START_TO_START, nodeStartRange) == 1;
24909         
24910          
24911     },
24912     rangeCompareNode : function(range, node)
24913     {
24914         var nodeRange = node.ownerDocument.createRange();
24915         try {
24916             nodeRange.selectNode(node);
24917         } catch (e) {
24918             nodeRange.selectNodeContents(node);
24919         }
24920         
24921         
24922         range.collapse(true);
24923     
24924         nodeRange.collapse(true);
24925      
24926         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24927         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24928          
24929         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24930         
24931         var nodeIsBefore   =  ss == 1;
24932         var nodeIsAfter    = ee == -1;
24933         
24934         if (nodeIsBefore && nodeIsAfter) {
24935             return 0; // outer
24936         }
24937         if (!nodeIsBefore && nodeIsAfter) {
24938             return 1; //right trailed.
24939         }
24940         
24941         if (nodeIsBefore && !nodeIsAfter) {
24942             return 2;  // left trailed.
24943         }
24944         // fully contined.
24945         return 3;
24946     },
24947
24948     // private? - in a new class?
24949     cleanUpPaste :  function()
24950     {
24951         // cleans up the whole document..
24952         Roo.log('cleanuppaste');
24953         
24954         this.cleanUpChildren(this.doc.body);
24955         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24956         if (clean != this.doc.body.innerHTML) {
24957             this.doc.body.innerHTML = clean;
24958         }
24959         
24960     },
24961     
24962     cleanWordChars : function(input) {// change the chars to hex code
24963         var he = Roo.HtmlEditorCore;
24964         
24965         var output = input;
24966         Roo.each(he.swapCodes, function(sw) { 
24967             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24968             
24969             output = output.replace(swapper, sw[1]);
24970         });
24971         
24972         return output;
24973     },
24974     
24975     
24976     cleanUpChildren : function (n)
24977     {
24978         if (!n.childNodes.length) {
24979             return;
24980         }
24981         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24982            this.cleanUpChild(n.childNodes[i]);
24983         }
24984     },
24985     
24986     
24987         
24988     
24989     cleanUpChild : function (node)
24990     {
24991         var ed = this;
24992         //console.log(node);
24993         if (node.nodeName == "#text") {
24994             // clean up silly Windows -- stuff?
24995             return; 
24996         }
24997         if (node.nodeName == "#comment") {
24998             node.parentNode.removeChild(node);
24999             // clean up silly Windows -- stuff?
25000             return; 
25001         }
25002         var lcname = node.tagName.toLowerCase();
25003         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25004         // whitelist of tags..
25005         
25006         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25007             // remove node.
25008             node.parentNode.removeChild(node);
25009             return;
25010             
25011         }
25012         
25013         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25014         
25015         // spans with no attributes - just remove them..
25016         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25017             remove_keep_children = true;
25018         }
25019         
25020         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25021         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25022         
25023         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25024         //    remove_keep_children = true;
25025         //}
25026         
25027         if (remove_keep_children) {
25028             this.cleanUpChildren(node);
25029             // inserts everything just before this node...
25030             while (node.childNodes.length) {
25031                 var cn = node.childNodes[0];
25032                 node.removeChild(cn);
25033                 node.parentNode.insertBefore(cn, node);
25034             }
25035             node.parentNode.removeChild(node);
25036             return;
25037         }
25038         
25039         if (!node.attributes || !node.attributes.length) {
25040             
25041           
25042             
25043             
25044             this.cleanUpChildren(node);
25045             return;
25046         }
25047         
25048         function cleanAttr(n,v)
25049         {
25050             
25051             if (v.match(/^\./) || v.match(/^\//)) {
25052                 return;
25053             }
25054             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25055                 return;
25056             }
25057             if (v.match(/^#/)) {
25058                 return;
25059             }
25060             if (v.match(/^\{/)) { // allow template editing.
25061                 return;
25062             }
25063 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25064             node.removeAttribute(n);
25065             
25066         }
25067         
25068         var cwhite = this.cwhite;
25069         var cblack = this.cblack;
25070             
25071         function cleanStyle(n,v)
25072         {
25073             if (v.match(/expression/)) { //XSS?? should we even bother..
25074                 node.removeAttribute(n);
25075                 return;
25076             }
25077             
25078             var parts = v.split(/;/);
25079             var clean = [];
25080             
25081             Roo.each(parts, function(p) {
25082                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25083                 if (!p.length) {
25084                     return true;
25085                 }
25086                 var l = p.split(':').shift().replace(/\s+/g,'');
25087                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25088                 
25089                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25090 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25091                     //node.removeAttribute(n);
25092                     return true;
25093                 }
25094                 //Roo.log()
25095                 // only allow 'c whitelisted system attributes'
25096                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25097 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25098                     //node.removeAttribute(n);
25099                     return true;
25100                 }
25101                 
25102                 
25103                  
25104                 
25105                 clean.push(p);
25106                 return true;
25107             });
25108             if (clean.length) { 
25109                 node.setAttribute(n, clean.join(';'));
25110             } else {
25111                 node.removeAttribute(n);
25112             }
25113             
25114         }
25115         
25116         
25117         for (var i = node.attributes.length-1; i > -1 ; i--) {
25118             var a = node.attributes[i];
25119             //console.log(a);
25120             
25121             if (a.name.toLowerCase().substr(0,2)=='on')  {
25122                 node.removeAttribute(a.name);
25123                 continue;
25124             }
25125             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25126                 node.removeAttribute(a.name);
25127                 continue;
25128             }
25129             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25130                 cleanAttr(a.name,a.value); // fixme..
25131                 continue;
25132             }
25133             if (a.name == 'style') {
25134                 cleanStyle(a.name,a.value);
25135                 continue;
25136             }
25137             /// clean up MS crap..
25138             // tecnically this should be a list of valid class'es..
25139             
25140             
25141             if (a.name == 'class') {
25142                 if (a.value.match(/^Mso/)) {
25143                     node.removeAttribute('class');
25144                 }
25145                 
25146                 if (a.value.match(/^body$/)) {
25147                     node.removeAttribute('class');
25148                 }
25149                 continue;
25150             }
25151             
25152             // style cleanup!?
25153             // class cleanup?
25154             
25155         }
25156         
25157         
25158         this.cleanUpChildren(node);
25159         
25160         
25161     },
25162     
25163     /**
25164      * Clean up MS wordisms...
25165      */
25166     cleanWord : function(node)
25167     {
25168         if (!node) {
25169             this.cleanWord(this.doc.body);
25170             return;
25171         }
25172         
25173         if(
25174                 node.nodeName == 'SPAN' &&
25175                 !node.hasAttributes() &&
25176                 node.childNodes.length == 1 &&
25177                 node.firstChild.nodeName == "#text"  
25178         ) {
25179             var textNode = node.firstChild;
25180             node.removeChild(textNode);
25181             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25182                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25183             }
25184             node.parentNode.insertBefore(textNode, node);
25185             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25186                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25187             }
25188             node.parentNode.removeChild(node);
25189         }
25190         
25191         if (node.nodeName == "#text") {
25192             // clean up silly Windows -- stuff?
25193             return; 
25194         }
25195         if (node.nodeName == "#comment") {
25196             node.parentNode.removeChild(node);
25197             // clean up silly Windows -- stuff?
25198             return; 
25199         }
25200         
25201         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25202             node.parentNode.removeChild(node);
25203             return;
25204         }
25205         //Roo.log(node.tagName);
25206         // remove - but keep children..
25207         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25208             //Roo.log('-- removed');
25209             while (node.childNodes.length) {
25210                 var cn = node.childNodes[0];
25211                 node.removeChild(cn);
25212                 node.parentNode.insertBefore(cn, node);
25213                 // move node to parent - and clean it..
25214                 this.cleanWord(cn);
25215             }
25216             node.parentNode.removeChild(node);
25217             /// no need to iterate chidlren = it's got none..
25218             //this.iterateChildren(node, this.cleanWord);
25219             return;
25220         }
25221         // clean styles
25222         if (node.className.length) {
25223             
25224             var cn = node.className.split(/\W+/);
25225             var cna = [];
25226             Roo.each(cn, function(cls) {
25227                 if (cls.match(/Mso[a-zA-Z]+/)) {
25228                     return;
25229                 }
25230                 cna.push(cls);
25231             });
25232             node.className = cna.length ? cna.join(' ') : '';
25233             if (!cna.length) {
25234                 node.removeAttribute("class");
25235             }
25236         }
25237         
25238         if (node.hasAttribute("lang")) {
25239             node.removeAttribute("lang");
25240         }
25241         
25242         if (node.hasAttribute("style")) {
25243             
25244             var styles = node.getAttribute("style").split(";");
25245             var nstyle = [];
25246             Roo.each(styles, function(s) {
25247                 if (!s.match(/:/)) {
25248                     return;
25249                 }
25250                 var kv = s.split(":");
25251                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25252                     return;
25253                 }
25254                 // what ever is left... we allow.
25255                 nstyle.push(s);
25256             });
25257             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25258             if (!nstyle.length) {
25259                 node.removeAttribute('style');
25260             }
25261         }
25262         this.iterateChildren(node, this.cleanWord);
25263         
25264         
25265         
25266     },
25267     /**
25268      * iterateChildren of a Node, calling fn each time, using this as the scole..
25269      * @param {DomNode} node node to iterate children of.
25270      * @param {Function} fn method of this class to call on each item.
25271      */
25272     iterateChildren : function(node, fn)
25273     {
25274         if (!node.childNodes.length) {
25275                 return;
25276         }
25277         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25278            fn.call(this, node.childNodes[i])
25279         }
25280     },
25281     
25282     
25283     /**
25284      * cleanTableWidths.
25285      *
25286      * Quite often pasting from word etc.. results in tables with column and widths.
25287      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25288      *
25289      */
25290     cleanTableWidths : function(node)
25291     {
25292          
25293          
25294         if (!node) {
25295             this.cleanTableWidths(this.doc.body);
25296             return;
25297         }
25298         
25299         // ignore list...
25300         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25301             return; 
25302         }
25303         Roo.log(node.tagName);
25304         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25305             this.iterateChildren(node, this.cleanTableWidths);
25306             return;
25307         }
25308         if (node.hasAttribute('width')) {
25309             node.removeAttribute('width');
25310         }
25311         
25312          
25313         if (node.hasAttribute("style")) {
25314             // pretty basic...
25315             
25316             var styles = node.getAttribute("style").split(";");
25317             var nstyle = [];
25318             Roo.each(styles, function(s) {
25319                 if (!s.match(/:/)) {
25320                     return;
25321                 }
25322                 var kv = s.split(":");
25323                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25324                     return;
25325                 }
25326                 // what ever is left... we allow.
25327                 nstyle.push(s);
25328             });
25329             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25330             if (!nstyle.length) {
25331                 node.removeAttribute('style');
25332             }
25333         }
25334         
25335         this.iterateChildren(node, this.cleanTableWidths);
25336         
25337         
25338     },
25339     
25340     
25341     
25342     
25343     domToHTML : function(currentElement, depth, nopadtext) {
25344         
25345         depth = depth || 0;
25346         nopadtext = nopadtext || false;
25347     
25348         if (!currentElement) {
25349             return this.domToHTML(this.doc.body);
25350         }
25351         
25352         //Roo.log(currentElement);
25353         var j;
25354         var allText = false;
25355         var nodeName = currentElement.nodeName;
25356         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25357         
25358         if  (nodeName == '#text') {
25359             
25360             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25361         }
25362         
25363         
25364         var ret = '';
25365         if (nodeName != 'BODY') {
25366              
25367             var i = 0;
25368             // Prints the node tagName, such as <A>, <IMG>, etc
25369             if (tagName) {
25370                 var attr = [];
25371                 for(i = 0; i < currentElement.attributes.length;i++) {
25372                     // quoting?
25373                     var aname = currentElement.attributes.item(i).name;
25374                     if (!currentElement.attributes.item(i).value.length) {
25375                         continue;
25376                     }
25377                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25378                 }
25379                 
25380                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25381             } 
25382             else {
25383                 
25384                 // eack
25385             }
25386         } else {
25387             tagName = false;
25388         }
25389         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25390             return ret;
25391         }
25392         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25393             nopadtext = true;
25394         }
25395         
25396         
25397         // Traverse the tree
25398         i = 0;
25399         var currentElementChild = currentElement.childNodes.item(i);
25400         var allText = true;
25401         var innerHTML  = '';
25402         lastnode = '';
25403         while (currentElementChild) {
25404             // Formatting code (indent the tree so it looks nice on the screen)
25405             var nopad = nopadtext;
25406             if (lastnode == 'SPAN') {
25407                 nopad  = true;
25408             }
25409             // text
25410             if  (currentElementChild.nodeName == '#text') {
25411                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25412                 toadd = nopadtext ? toadd : toadd.trim();
25413                 if (!nopad && toadd.length > 80) {
25414                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25415                 }
25416                 innerHTML  += toadd;
25417                 
25418                 i++;
25419                 currentElementChild = currentElement.childNodes.item(i);
25420                 lastNode = '';
25421                 continue;
25422             }
25423             allText = false;
25424             
25425             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25426                 
25427             // Recursively traverse the tree structure of the child node
25428             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25429             lastnode = currentElementChild.nodeName;
25430             i++;
25431             currentElementChild=currentElement.childNodes.item(i);
25432         }
25433         
25434         ret += innerHTML;
25435         
25436         if (!allText) {
25437                 // The remaining code is mostly for formatting the tree
25438             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25439         }
25440         
25441         
25442         if (tagName) {
25443             ret+= "</"+tagName+">";
25444         }
25445         return ret;
25446         
25447     },
25448         
25449     applyBlacklists : function()
25450     {
25451         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25452         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25453         
25454         this.white = [];
25455         this.black = [];
25456         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25457             if (b.indexOf(tag) > -1) {
25458                 return;
25459             }
25460             this.white.push(tag);
25461             
25462         }, this);
25463         
25464         Roo.each(w, function(tag) {
25465             if (b.indexOf(tag) > -1) {
25466                 return;
25467             }
25468             if (this.white.indexOf(tag) > -1) {
25469                 return;
25470             }
25471             this.white.push(tag);
25472             
25473         }, this);
25474         
25475         
25476         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25477             if (w.indexOf(tag) > -1) {
25478                 return;
25479             }
25480             this.black.push(tag);
25481             
25482         }, this);
25483         
25484         Roo.each(b, function(tag) {
25485             if (w.indexOf(tag) > -1) {
25486                 return;
25487             }
25488             if (this.black.indexOf(tag) > -1) {
25489                 return;
25490             }
25491             this.black.push(tag);
25492             
25493         }, this);
25494         
25495         
25496         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25497         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25498         
25499         this.cwhite = [];
25500         this.cblack = [];
25501         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25502             if (b.indexOf(tag) > -1) {
25503                 return;
25504             }
25505             this.cwhite.push(tag);
25506             
25507         }, this);
25508         
25509         Roo.each(w, function(tag) {
25510             if (b.indexOf(tag) > -1) {
25511                 return;
25512             }
25513             if (this.cwhite.indexOf(tag) > -1) {
25514                 return;
25515             }
25516             this.cwhite.push(tag);
25517             
25518         }, this);
25519         
25520         
25521         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25522             if (w.indexOf(tag) > -1) {
25523                 return;
25524             }
25525             this.cblack.push(tag);
25526             
25527         }, this);
25528         
25529         Roo.each(b, function(tag) {
25530             if (w.indexOf(tag) > -1) {
25531                 return;
25532             }
25533             if (this.cblack.indexOf(tag) > -1) {
25534                 return;
25535             }
25536             this.cblack.push(tag);
25537             
25538         }, this);
25539     },
25540     
25541     setStylesheets : function(stylesheets)
25542     {
25543         if(typeof(stylesheets) == 'string'){
25544             Roo.get(this.iframe.contentDocument.head).createChild({
25545                 tag : 'link',
25546                 rel : 'stylesheet',
25547                 type : 'text/css',
25548                 href : stylesheets
25549             });
25550             
25551             return;
25552         }
25553         var _this = this;
25554      
25555         Roo.each(stylesheets, function(s) {
25556             if(!s.length){
25557                 return;
25558             }
25559             
25560             Roo.get(_this.iframe.contentDocument.head).createChild({
25561                 tag : 'link',
25562                 rel : 'stylesheet',
25563                 type : 'text/css',
25564                 href : s
25565             });
25566         });
25567
25568         
25569     },
25570     
25571     removeStylesheets : function()
25572     {
25573         var _this = this;
25574         
25575         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25576             s.remove();
25577         });
25578     },
25579     
25580     setStyle : function(style)
25581     {
25582         Roo.get(this.iframe.contentDocument.head).createChild({
25583             tag : 'style',
25584             type : 'text/css',
25585             html : style
25586         });
25587
25588         return;
25589     }
25590     
25591     // hide stuff that is not compatible
25592     /**
25593      * @event blur
25594      * @hide
25595      */
25596     /**
25597      * @event change
25598      * @hide
25599      */
25600     /**
25601      * @event focus
25602      * @hide
25603      */
25604     /**
25605      * @event specialkey
25606      * @hide
25607      */
25608     /**
25609      * @cfg {String} fieldClass @hide
25610      */
25611     /**
25612      * @cfg {String} focusClass @hide
25613      */
25614     /**
25615      * @cfg {String} autoCreate @hide
25616      */
25617     /**
25618      * @cfg {String} inputType @hide
25619      */
25620     /**
25621      * @cfg {String} invalidClass @hide
25622      */
25623     /**
25624      * @cfg {String} invalidText @hide
25625      */
25626     /**
25627      * @cfg {String} msgFx @hide
25628      */
25629     /**
25630      * @cfg {String} validateOnBlur @hide
25631      */
25632 });
25633
25634 Roo.HtmlEditorCore.white = [
25635         'area', 'br', 'img', 'input', 'hr', 'wbr',
25636         
25637        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25638        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25639        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25640        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25641        'table',   'ul',         'xmp', 
25642        
25643        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25644       'thead',   'tr', 
25645      
25646       'dir', 'menu', 'ol', 'ul', 'dl',
25647        
25648       'embed',  'object'
25649 ];
25650
25651
25652 Roo.HtmlEditorCore.black = [
25653     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25654         'applet', // 
25655         'base',   'basefont', 'bgsound', 'blink',  'body', 
25656         'frame',  'frameset', 'head',    'html',   'ilayer', 
25657         'iframe', 'layer',  'link',     'meta',    'object',   
25658         'script', 'style' ,'title',  'xml' // clean later..
25659 ];
25660 Roo.HtmlEditorCore.clean = [
25661     'script', 'style', 'title', 'xml'
25662 ];
25663 Roo.HtmlEditorCore.remove = [
25664     'font'
25665 ];
25666 // attributes..
25667
25668 Roo.HtmlEditorCore.ablack = [
25669     'on'
25670 ];
25671     
25672 Roo.HtmlEditorCore.aclean = [ 
25673     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25674 ];
25675
25676 // protocols..
25677 Roo.HtmlEditorCore.pwhite= [
25678         'http',  'https',  'mailto'
25679 ];
25680
25681 // white listed style attributes.
25682 Roo.HtmlEditorCore.cwhite= [
25683       //  'text-align', /// default is to allow most things..
25684       
25685          
25686 //        'font-size'//??
25687 ];
25688
25689 // black listed style attributes.
25690 Roo.HtmlEditorCore.cblack= [
25691       //  'font-size' -- this can be set by the project 
25692 ];
25693
25694
25695 Roo.HtmlEditorCore.swapCodes   =[ 
25696     [    8211, "--" ], 
25697     [    8212, "--" ], 
25698     [    8216,  "'" ],  
25699     [    8217, "'" ],  
25700     [    8220, '"' ],  
25701     [    8221, '"' ],  
25702     [    8226, "*" ],  
25703     [    8230, "..." ]
25704 ]; 
25705
25706     /*
25707  * - LGPL
25708  *
25709  * HtmlEditor
25710  * 
25711  */
25712
25713 /**
25714  * @class Roo.bootstrap.HtmlEditor
25715  * @extends Roo.bootstrap.TextArea
25716  * Bootstrap HtmlEditor class
25717
25718  * @constructor
25719  * Create a new HtmlEditor
25720  * @param {Object} config The config object
25721  */
25722
25723 Roo.bootstrap.HtmlEditor = function(config){
25724     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25725     if (!this.toolbars) {
25726         this.toolbars = [];
25727     }
25728     
25729     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25730     this.addEvents({
25731             /**
25732              * @event initialize
25733              * Fires when the editor is fully initialized (including the iframe)
25734              * @param {HtmlEditor} this
25735              */
25736             initialize: true,
25737             /**
25738              * @event activate
25739              * Fires when the editor is first receives the focus. Any insertion must wait
25740              * until after this event.
25741              * @param {HtmlEditor} this
25742              */
25743             activate: true,
25744              /**
25745              * @event beforesync
25746              * Fires before the textarea is updated with content from the editor iframe. Return false
25747              * to cancel the sync.
25748              * @param {HtmlEditor} this
25749              * @param {String} html
25750              */
25751             beforesync: true,
25752              /**
25753              * @event beforepush
25754              * Fires before the iframe editor is updated with content from the textarea. Return false
25755              * to cancel the push.
25756              * @param {HtmlEditor} this
25757              * @param {String} html
25758              */
25759             beforepush: true,
25760              /**
25761              * @event sync
25762              * Fires when the textarea is updated with content from the editor iframe.
25763              * @param {HtmlEditor} this
25764              * @param {String} html
25765              */
25766             sync: true,
25767              /**
25768              * @event push
25769              * Fires when the iframe editor is updated with content from the textarea.
25770              * @param {HtmlEditor} this
25771              * @param {String} html
25772              */
25773             push: true,
25774              /**
25775              * @event editmodechange
25776              * Fires when the editor switches edit modes
25777              * @param {HtmlEditor} this
25778              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25779              */
25780             editmodechange: true,
25781             /**
25782              * @event editorevent
25783              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25784              * @param {HtmlEditor} this
25785              */
25786             editorevent: true,
25787             /**
25788              * @event firstfocus
25789              * Fires when on first focus - needed by toolbars..
25790              * @param {HtmlEditor} this
25791              */
25792             firstfocus: true,
25793             /**
25794              * @event autosave
25795              * Auto save the htmlEditor value as a file into Events
25796              * @param {HtmlEditor} this
25797              */
25798             autosave: true,
25799             /**
25800              * @event savedpreview
25801              * preview the saved version of htmlEditor
25802              * @param {HtmlEditor} this
25803              */
25804             savedpreview: true
25805         });
25806 };
25807
25808
25809 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25810     
25811     
25812       /**
25813      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25814      */
25815     toolbars : false,
25816     
25817      /**
25818     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25819     */
25820     btns : [],
25821    
25822      /**
25823      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25824      *                        Roo.resizable.
25825      */
25826     resizable : false,
25827      /**
25828      * @cfg {Number} height (in pixels)
25829      */   
25830     height: 300,
25831    /**
25832      * @cfg {Number} width (in pixels)
25833      */   
25834     width: false,
25835     
25836     /**
25837      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25838      * 
25839      */
25840     stylesheets: false,
25841     
25842     // id of frame..
25843     frameId: false,
25844     
25845     // private properties
25846     validationEvent : false,
25847     deferHeight: true,
25848     initialized : false,
25849     activated : false,
25850     
25851     onFocus : Roo.emptyFn,
25852     iframePad:3,
25853     hideMode:'offsets',
25854     
25855     tbContainer : false,
25856     
25857     bodyCls : '',
25858     
25859     toolbarContainer :function() {
25860         return this.wrap.select('.x-html-editor-tb',true).first();
25861     },
25862
25863     /**
25864      * Protected method that will not generally be called directly. It
25865      * is called when the editor creates its toolbar. Override this method if you need to
25866      * add custom toolbar buttons.
25867      * @param {HtmlEditor} editor
25868      */
25869     createToolbar : function(){
25870         Roo.log('renewing');
25871         Roo.log("create toolbars");
25872         
25873         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25874         this.toolbars[0].render(this.toolbarContainer());
25875         
25876         return;
25877         
25878 //        if (!editor.toolbars || !editor.toolbars.length) {
25879 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25880 //        }
25881 //        
25882 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25883 //            editor.toolbars[i] = Roo.factory(
25884 //                    typeof(editor.toolbars[i]) == 'string' ?
25885 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25886 //                Roo.bootstrap.HtmlEditor);
25887 //            editor.toolbars[i].init(editor);
25888 //        }
25889     },
25890
25891      
25892     // private
25893     onRender : function(ct, position)
25894     {
25895        // Roo.log("Call onRender: " + this.xtype);
25896         var _t = this;
25897         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25898       
25899         this.wrap = this.inputEl().wrap({
25900             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25901         });
25902         
25903         this.editorcore.onRender(ct, position);
25904          
25905         if (this.resizable) {
25906             this.resizeEl = new Roo.Resizable(this.wrap, {
25907                 pinned : true,
25908                 wrap: true,
25909                 dynamic : true,
25910                 minHeight : this.height,
25911                 height: this.height,
25912                 handles : this.resizable,
25913                 width: this.width,
25914                 listeners : {
25915                     resize : function(r, w, h) {
25916                         _t.onResize(w,h); // -something
25917                     }
25918                 }
25919             });
25920             
25921         }
25922         this.createToolbar(this);
25923        
25924         
25925         if(!this.width && this.resizable){
25926             this.setSize(this.wrap.getSize());
25927         }
25928         if (this.resizeEl) {
25929             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25930             // should trigger onReize..
25931         }
25932         
25933     },
25934
25935     // private
25936     onResize : function(w, h)
25937     {
25938         Roo.log('resize: ' +w + ',' + h );
25939         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25940         var ew = false;
25941         var eh = false;
25942         
25943         if(this.inputEl() ){
25944             if(typeof w == 'number'){
25945                 var aw = w - this.wrap.getFrameWidth('lr');
25946                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25947                 ew = aw;
25948             }
25949             if(typeof h == 'number'){
25950                  var tbh = -11;  // fixme it needs to tool bar size!
25951                 for (var i =0; i < this.toolbars.length;i++) {
25952                     // fixme - ask toolbars for heights?
25953                     tbh += this.toolbars[i].el.getHeight();
25954                     //if (this.toolbars[i].footer) {
25955                     //    tbh += this.toolbars[i].footer.el.getHeight();
25956                     //}
25957                 }
25958               
25959                 
25960                 
25961                 
25962                 
25963                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25964                 ah -= 5; // knock a few pixes off for look..
25965                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25966                 var eh = ah;
25967             }
25968         }
25969         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25970         this.editorcore.onResize(ew,eh);
25971         
25972     },
25973
25974     /**
25975      * Toggles the editor between standard and source edit mode.
25976      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25977      */
25978     toggleSourceEdit : function(sourceEditMode)
25979     {
25980         this.editorcore.toggleSourceEdit(sourceEditMode);
25981         
25982         if(this.editorcore.sourceEditMode){
25983             Roo.log('editor - showing textarea');
25984             
25985 //            Roo.log('in');
25986 //            Roo.log(this.syncValue());
25987             this.syncValue();
25988             this.inputEl().removeClass(['hide', 'x-hidden']);
25989             this.inputEl().dom.removeAttribute('tabIndex');
25990             this.inputEl().focus();
25991         }else{
25992             Roo.log('editor - hiding textarea');
25993 //            Roo.log('out')
25994 //            Roo.log(this.pushValue()); 
25995             this.pushValue();
25996             
25997             this.inputEl().addClass(['hide', 'x-hidden']);
25998             this.inputEl().dom.setAttribute('tabIndex', -1);
25999             //this.deferFocus();
26000         }
26001          
26002         if(this.resizable){
26003             this.setSize(this.wrap.getSize());
26004         }
26005         
26006         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26007     },
26008  
26009     // private (for BoxComponent)
26010     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26011
26012     // private (for BoxComponent)
26013     getResizeEl : function(){
26014         return this.wrap;
26015     },
26016
26017     // private (for BoxComponent)
26018     getPositionEl : function(){
26019         return this.wrap;
26020     },
26021
26022     // private
26023     initEvents : function(){
26024         this.originalValue = this.getValue();
26025     },
26026
26027 //    /**
26028 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26029 //     * @method
26030 //     */
26031 //    markInvalid : Roo.emptyFn,
26032 //    /**
26033 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26034 //     * @method
26035 //     */
26036 //    clearInvalid : Roo.emptyFn,
26037
26038     setValue : function(v){
26039         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26040         this.editorcore.pushValue();
26041     },
26042
26043      
26044     // private
26045     deferFocus : function(){
26046         this.focus.defer(10, this);
26047     },
26048
26049     // doc'ed in Field
26050     focus : function(){
26051         this.editorcore.focus();
26052         
26053     },
26054       
26055
26056     // private
26057     onDestroy : function(){
26058         
26059         
26060         
26061         if(this.rendered){
26062             
26063             for (var i =0; i < this.toolbars.length;i++) {
26064                 // fixme - ask toolbars for heights?
26065                 this.toolbars[i].onDestroy();
26066             }
26067             
26068             this.wrap.dom.innerHTML = '';
26069             this.wrap.remove();
26070         }
26071     },
26072
26073     // private
26074     onFirstFocus : function(){
26075         //Roo.log("onFirstFocus");
26076         this.editorcore.onFirstFocus();
26077          for (var i =0; i < this.toolbars.length;i++) {
26078             this.toolbars[i].onFirstFocus();
26079         }
26080         
26081     },
26082     
26083     // private
26084     syncValue : function()
26085     {   
26086         this.editorcore.syncValue();
26087     },
26088     
26089     pushValue : function()
26090     {   
26091         this.editorcore.pushValue();
26092     }
26093      
26094     
26095     // hide stuff that is not compatible
26096     /**
26097      * @event blur
26098      * @hide
26099      */
26100     /**
26101      * @event change
26102      * @hide
26103      */
26104     /**
26105      * @event focus
26106      * @hide
26107      */
26108     /**
26109      * @event specialkey
26110      * @hide
26111      */
26112     /**
26113      * @cfg {String} fieldClass @hide
26114      */
26115     /**
26116      * @cfg {String} focusClass @hide
26117      */
26118     /**
26119      * @cfg {String} autoCreate @hide
26120      */
26121     /**
26122      * @cfg {String} inputType @hide
26123      */
26124      
26125     /**
26126      * @cfg {String} invalidText @hide
26127      */
26128     /**
26129      * @cfg {String} msgFx @hide
26130      */
26131     /**
26132      * @cfg {String} validateOnBlur @hide
26133      */
26134 });
26135  
26136     
26137    
26138    
26139    
26140       
26141 Roo.namespace('Roo.bootstrap.htmleditor');
26142 /**
26143  * @class Roo.bootstrap.HtmlEditorToolbar1
26144  * Basic Toolbar
26145  * 
26146  * @example
26147  * Usage:
26148  *
26149  new Roo.bootstrap.HtmlEditor({
26150     ....
26151     toolbars : [
26152         new Roo.bootstrap.HtmlEditorToolbar1({
26153             disable : { fonts: 1 , format: 1, ..., ... , ...],
26154             btns : [ .... ]
26155         })
26156     }
26157      
26158  * 
26159  * @cfg {Object} disable List of elements to disable..
26160  * @cfg {Array} btns List of additional buttons.
26161  * 
26162  * 
26163  * NEEDS Extra CSS? 
26164  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26165  */
26166  
26167 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26168 {
26169     
26170     Roo.apply(this, config);
26171     
26172     // default disabled, based on 'good practice'..
26173     this.disable = this.disable || {};
26174     Roo.applyIf(this.disable, {
26175         fontSize : true,
26176         colors : true,
26177         specialElements : true
26178     });
26179     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26180     
26181     this.editor = config.editor;
26182     this.editorcore = config.editor.editorcore;
26183     
26184     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26185     
26186     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26187     // dont call parent... till later.
26188 }
26189 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26190      
26191     bar : true,
26192     
26193     editor : false,
26194     editorcore : false,
26195     
26196     
26197     formats : [
26198         "p" ,  
26199         "h1","h2","h3","h4","h5","h6", 
26200         "pre", "code", 
26201         "abbr", "acronym", "address", "cite", "samp", "var",
26202         'div','span'
26203     ],
26204     
26205     onRender : function(ct, position)
26206     {
26207        // Roo.log("Call onRender: " + this.xtype);
26208         
26209        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26210        Roo.log(this.el);
26211        this.el.dom.style.marginBottom = '0';
26212        var _this = this;
26213        var editorcore = this.editorcore;
26214        var editor= this.editor;
26215        
26216        var children = [];
26217        var btn = function(id,cmd , toggle, handler, html){
26218        
26219             var  event = toggle ? 'toggle' : 'click';
26220        
26221             var a = {
26222                 size : 'sm',
26223                 xtype: 'Button',
26224                 xns: Roo.bootstrap,
26225                 //glyphicon : id,
26226                 fa: id,
26227                 cmd : id || cmd,
26228                 enableToggle:toggle !== false,
26229                 html : html || '',
26230                 pressed : toggle ? false : null,
26231                 listeners : {}
26232             };
26233             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26234                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26235             };
26236             children.push(a);
26237             return a;
26238        }
26239        
26240     //    var cb_box = function...
26241         
26242         var style = {
26243                 xtype: 'Button',
26244                 size : 'sm',
26245                 xns: Roo.bootstrap,
26246                 fa : 'font',
26247                 //html : 'submit'
26248                 menu : {
26249                     xtype: 'Menu',
26250                     xns: Roo.bootstrap,
26251                     items:  []
26252                 }
26253         };
26254         Roo.each(this.formats, function(f) {
26255             style.menu.items.push({
26256                 xtype :'MenuItem',
26257                 xns: Roo.bootstrap,
26258                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26259                 tagname : f,
26260                 listeners : {
26261                     click : function()
26262                     {
26263                         editorcore.insertTag(this.tagname);
26264                         editor.focus();
26265                     }
26266                 }
26267                 
26268             });
26269         });
26270         children.push(style);   
26271         
26272         btn('bold',false,true);
26273         btn('italic',false,true);
26274         btn('align-left', 'justifyleft',true);
26275         btn('align-center', 'justifycenter',true);
26276         btn('align-right' , 'justifyright',true);
26277         btn('link', false, false, function(btn) {
26278             //Roo.log("create link?");
26279             var url = prompt(this.createLinkText, this.defaultLinkValue);
26280             if(url && url != 'http:/'+'/'){
26281                 this.editorcore.relayCmd('createlink', url);
26282             }
26283         }),
26284         btn('list','insertunorderedlist',true);
26285         btn('pencil', false,true, function(btn){
26286                 Roo.log(this);
26287                 this.toggleSourceEdit(btn.pressed);
26288         });
26289         
26290         if (this.editor.btns.length > 0) {
26291             for (var i = 0; i<this.editor.btns.length; i++) {
26292                 children.push(this.editor.btns[i]);
26293             }
26294         }
26295         
26296         /*
26297         var cog = {
26298                 xtype: 'Button',
26299                 size : 'sm',
26300                 xns: Roo.bootstrap,
26301                 glyphicon : 'cog',
26302                 //html : 'submit'
26303                 menu : {
26304                     xtype: 'Menu',
26305                     xns: Roo.bootstrap,
26306                     items:  []
26307                 }
26308         };
26309         
26310         cog.menu.items.push({
26311             xtype :'MenuItem',
26312             xns: Roo.bootstrap,
26313             html : Clean styles,
26314             tagname : f,
26315             listeners : {
26316                 click : function()
26317                 {
26318                     editorcore.insertTag(this.tagname);
26319                     editor.focus();
26320                 }
26321             }
26322             
26323         });
26324        */
26325         
26326          
26327        this.xtype = 'NavSimplebar';
26328         
26329         for(var i=0;i< children.length;i++) {
26330             
26331             this.buttons.add(this.addxtypeChild(children[i]));
26332             
26333         }
26334         
26335         editor.on('editorevent', this.updateToolbar, this);
26336     },
26337     onBtnClick : function(id)
26338     {
26339        this.editorcore.relayCmd(id);
26340        this.editorcore.focus();
26341     },
26342     
26343     /**
26344      * Protected method that will not generally be called directly. It triggers
26345      * a toolbar update by reading the markup state of the current selection in the editor.
26346      */
26347     updateToolbar: function(){
26348
26349         if(!this.editorcore.activated){
26350             this.editor.onFirstFocus(); // is this neeed?
26351             return;
26352         }
26353
26354         var btns = this.buttons; 
26355         var doc = this.editorcore.doc;
26356         btns.get('bold').setActive(doc.queryCommandState('bold'));
26357         btns.get('italic').setActive(doc.queryCommandState('italic'));
26358         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26359         
26360         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26361         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26362         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26363         
26364         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26365         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26366          /*
26367         
26368         var ans = this.editorcore.getAllAncestors();
26369         if (this.formatCombo) {
26370             
26371             
26372             var store = this.formatCombo.store;
26373             this.formatCombo.setValue("");
26374             for (var i =0; i < ans.length;i++) {
26375                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26376                     // select it..
26377                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26378                     break;
26379                 }
26380             }
26381         }
26382         
26383         
26384         
26385         // hides menus... - so this cant be on a menu...
26386         Roo.bootstrap.MenuMgr.hideAll();
26387         */
26388         Roo.bootstrap.MenuMgr.hideAll();
26389         //this.editorsyncValue();
26390     },
26391     onFirstFocus: function() {
26392         this.buttons.each(function(item){
26393            item.enable();
26394         });
26395     },
26396     toggleSourceEdit : function(sourceEditMode){
26397         
26398           
26399         if(sourceEditMode){
26400             Roo.log("disabling buttons");
26401            this.buttons.each( function(item){
26402                 if(item.cmd != 'pencil'){
26403                     item.disable();
26404                 }
26405             });
26406           
26407         }else{
26408             Roo.log("enabling buttons");
26409             if(this.editorcore.initialized){
26410                 this.buttons.each( function(item){
26411                     item.enable();
26412                 });
26413             }
26414             
26415         }
26416         Roo.log("calling toggole on editor");
26417         // tell the editor that it's been pressed..
26418         this.editor.toggleSourceEdit(sourceEditMode);
26419        
26420     }
26421 });
26422
26423
26424
26425
26426  
26427 /*
26428  * - LGPL
26429  */
26430
26431 /**
26432  * @class Roo.bootstrap.Markdown
26433  * @extends Roo.bootstrap.TextArea
26434  * Bootstrap Showdown editable area
26435  * @cfg {string} content
26436  * 
26437  * @constructor
26438  * Create a new Showdown
26439  */
26440
26441 Roo.bootstrap.Markdown = function(config){
26442     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26443    
26444 };
26445
26446 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26447     
26448     editing :false,
26449     
26450     initEvents : function()
26451     {
26452         
26453         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26454         this.markdownEl = this.el.createChild({
26455             cls : 'roo-markdown-area'
26456         });
26457         this.inputEl().addClass('d-none');
26458         if (this.getValue() == '') {
26459             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26460             
26461         } else {
26462             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26463         }
26464         this.markdownEl.on('click', this.toggleTextEdit, this);
26465         this.on('blur', this.toggleTextEdit, this);
26466         this.on('specialkey', this.resizeTextArea, this);
26467     },
26468     
26469     toggleTextEdit : function()
26470     {
26471         var sh = this.markdownEl.getHeight();
26472         this.inputEl().addClass('d-none');
26473         this.markdownEl.addClass('d-none');
26474         if (!this.editing) {
26475             // show editor?
26476             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26477             this.inputEl().removeClass('d-none');
26478             this.inputEl().focus();
26479             this.editing = true;
26480             return;
26481         }
26482         // show showdown...
26483         this.updateMarkdown();
26484         this.markdownEl.removeClass('d-none');
26485         this.editing = false;
26486         return;
26487     },
26488     updateMarkdown : function()
26489     {
26490         if (this.getValue() == '') {
26491             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26492             return;
26493         }
26494  
26495         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26496     },
26497     
26498     resizeTextArea: function () {
26499         
26500         var sh = 100;
26501         Roo.log([sh, this.getValue().split("\n").length * 30]);
26502         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26503     },
26504     setValue : function(val)
26505     {
26506         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26507         if (!this.editing) {
26508             this.updateMarkdown();
26509         }
26510         
26511     },
26512     focus : function()
26513     {
26514         if (!this.editing) {
26515             this.toggleTextEdit();
26516         }
26517         
26518     }
26519
26520
26521 });
26522 /**
26523  * @class Roo.bootstrap.Table.AbstractSelectionModel
26524  * @extends Roo.util.Observable
26525  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26526  * implemented by descendant classes.  This class should not be directly instantiated.
26527  * @constructor
26528  */
26529 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26530     this.locked = false;
26531     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26532 };
26533
26534
26535 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26536     /** @ignore Called by the grid automatically. Do not call directly. */
26537     init : function(grid){
26538         this.grid = grid;
26539         this.initEvents();
26540     },
26541
26542     /**
26543      * Locks the selections.
26544      */
26545     lock : function(){
26546         this.locked = true;
26547     },
26548
26549     /**
26550      * Unlocks the selections.
26551      */
26552     unlock : function(){
26553         this.locked = false;
26554     },
26555
26556     /**
26557      * Returns true if the selections are locked.
26558      * @return {Boolean}
26559      */
26560     isLocked : function(){
26561         return this.locked;
26562     },
26563     
26564     
26565     initEvents : function ()
26566     {
26567         
26568     }
26569 });
26570 /**
26571  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26572  * @class Roo.bootstrap.Table.RowSelectionModel
26573  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26574  * It supports multiple selections and keyboard selection/navigation. 
26575  * @constructor
26576  * @param {Object} config
26577  */
26578
26579 Roo.bootstrap.Table.RowSelectionModel = function(config){
26580     Roo.apply(this, config);
26581     this.selections = new Roo.util.MixedCollection(false, function(o){
26582         return o.id;
26583     });
26584
26585     this.last = false;
26586     this.lastActive = false;
26587
26588     this.addEvents({
26589         /**
26590              * @event selectionchange
26591              * Fires when the selection changes
26592              * @param {SelectionModel} this
26593              */
26594             "selectionchange" : true,
26595         /**
26596              * @event afterselectionchange
26597              * Fires after the selection changes (eg. by key press or clicking)
26598              * @param {SelectionModel} this
26599              */
26600             "afterselectionchange" : true,
26601         /**
26602              * @event beforerowselect
26603              * Fires when a row is selected being selected, return false to cancel.
26604              * @param {SelectionModel} this
26605              * @param {Number} rowIndex The selected index
26606              * @param {Boolean} keepExisting False if other selections will be cleared
26607              */
26608             "beforerowselect" : true,
26609         /**
26610              * @event rowselect
26611              * Fires when a row is selected.
26612              * @param {SelectionModel} this
26613              * @param {Number} rowIndex The selected index
26614              * @param {Roo.data.Record} r The record
26615              */
26616             "rowselect" : true,
26617         /**
26618              * @event rowdeselect
26619              * Fires when a row is deselected.
26620              * @param {SelectionModel} this
26621              * @param {Number} rowIndex The selected index
26622              */
26623         "rowdeselect" : true
26624     });
26625     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26626     this.locked = false;
26627  };
26628
26629 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26630     /**
26631      * @cfg {Boolean} singleSelect
26632      * True to allow selection of only one row at a time (defaults to false)
26633      */
26634     singleSelect : false,
26635
26636     // private
26637     initEvents : function()
26638     {
26639
26640         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26641         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26642         //}else{ // allow click to work like normal
26643          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26644         //}
26645         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26646         this.grid.on("rowclick", this.handleMouseDown, this);
26647         
26648         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26649             "up" : function(e){
26650                 if(!e.shiftKey){
26651                     this.selectPrevious(e.shiftKey);
26652                 }else if(this.last !== false && this.lastActive !== false){
26653                     var last = this.last;
26654                     this.selectRange(this.last,  this.lastActive-1);
26655                     this.grid.getView().focusRow(this.lastActive);
26656                     if(last !== false){
26657                         this.last = last;
26658                     }
26659                 }else{
26660                     this.selectFirstRow();
26661                 }
26662                 this.fireEvent("afterselectionchange", this);
26663             },
26664             "down" : function(e){
26665                 if(!e.shiftKey){
26666                     this.selectNext(e.shiftKey);
26667                 }else if(this.last !== false && this.lastActive !== false){
26668                     var last = this.last;
26669                     this.selectRange(this.last,  this.lastActive+1);
26670                     this.grid.getView().focusRow(this.lastActive);
26671                     if(last !== false){
26672                         this.last = last;
26673                     }
26674                 }else{
26675                     this.selectFirstRow();
26676                 }
26677                 this.fireEvent("afterselectionchange", this);
26678             },
26679             scope: this
26680         });
26681         this.grid.store.on('load', function(){
26682             this.selections.clear();
26683         },this);
26684         /*
26685         var view = this.grid.view;
26686         view.on("refresh", this.onRefresh, this);
26687         view.on("rowupdated", this.onRowUpdated, this);
26688         view.on("rowremoved", this.onRemove, this);
26689         */
26690     },
26691
26692     // private
26693     onRefresh : function()
26694     {
26695         var ds = this.grid.store, i, v = this.grid.view;
26696         var s = this.selections;
26697         s.each(function(r){
26698             if((i = ds.indexOfId(r.id)) != -1){
26699                 v.onRowSelect(i);
26700             }else{
26701                 s.remove(r);
26702             }
26703         });
26704     },
26705
26706     // private
26707     onRemove : function(v, index, r){
26708         this.selections.remove(r);
26709     },
26710
26711     // private
26712     onRowUpdated : function(v, index, r){
26713         if(this.isSelected(r)){
26714             v.onRowSelect(index);
26715         }
26716     },
26717
26718     /**
26719      * Select records.
26720      * @param {Array} records The records to select
26721      * @param {Boolean} keepExisting (optional) True to keep existing selections
26722      */
26723     selectRecords : function(records, keepExisting)
26724     {
26725         if(!keepExisting){
26726             this.clearSelections();
26727         }
26728             var ds = this.grid.store;
26729         for(var i = 0, len = records.length; i < len; i++){
26730             this.selectRow(ds.indexOf(records[i]), true);
26731         }
26732     },
26733
26734     /**
26735      * Gets the number of selected rows.
26736      * @return {Number}
26737      */
26738     getCount : function(){
26739         return this.selections.length;
26740     },
26741
26742     /**
26743      * Selects the first row in the grid.
26744      */
26745     selectFirstRow : function(){
26746         this.selectRow(0);
26747     },
26748
26749     /**
26750      * Select the last row.
26751      * @param {Boolean} keepExisting (optional) True to keep existing selections
26752      */
26753     selectLastRow : function(keepExisting){
26754         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26755         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26756     },
26757
26758     /**
26759      * Selects the row immediately following the last selected row.
26760      * @param {Boolean} keepExisting (optional) True to keep existing selections
26761      */
26762     selectNext : function(keepExisting)
26763     {
26764             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26765             this.selectRow(this.last+1, keepExisting);
26766             this.grid.getView().focusRow(this.last);
26767         }
26768     },
26769
26770     /**
26771      * Selects the row that precedes the last selected row.
26772      * @param {Boolean} keepExisting (optional) True to keep existing selections
26773      */
26774     selectPrevious : function(keepExisting){
26775         if(this.last){
26776             this.selectRow(this.last-1, keepExisting);
26777             this.grid.getView().focusRow(this.last);
26778         }
26779     },
26780
26781     /**
26782      * Returns the selected records
26783      * @return {Array} Array of selected records
26784      */
26785     getSelections : function(){
26786         return [].concat(this.selections.items);
26787     },
26788
26789     /**
26790      * Returns the first selected record.
26791      * @return {Record}
26792      */
26793     getSelected : function(){
26794         return this.selections.itemAt(0);
26795     },
26796
26797
26798     /**
26799      * Clears all selections.
26800      */
26801     clearSelections : function(fast)
26802     {
26803         if(this.locked) {
26804             return;
26805         }
26806         if(fast !== true){
26807                 var ds = this.grid.store;
26808             var s = this.selections;
26809             s.each(function(r){
26810                 this.deselectRow(ds.indexOfId(r.id));
26811             }, this);
26812             s.clear();
26813         }else{
26814             this.selections.clear();
26815         }
26816         this.last = false;
26817     },
26818
26819
26820     /**
26821      * Selects all rows.
26822      */
26823     selectAll : function(){
26824         if(this.locked) {
26825             return;
26826         }
26827         this.selections.clear();
26828         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26829             this.selectRow(i, true);
26830         }
26831     },
26832
26833     /**
26834      * Returns True if there is a selection.
26835      * @return {Boolean}
26836      */
26837     hasSelection : function(){
26838         return this.selections.length > 0;
26839     },
26840
26841     /**
26842      * Returns True if the specified row is selected.
26843      * @param {Number/Record} record The record or index of the record to check
26844      * @return {Boolean}
26845      */
26846     isSelected : function(index){
26847             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26848         return (r && this.selections.key(r.id) ? true : false);
26849     },
26850
26851     /**
26852      * Returns True if the specified record id is selected.
26853      * @param {String} id The id of record to check
26854      * @return {Boolean}
26855      */
26856     isIdSelected : function(id){
26857         return (this.selections.key(id) ? true : false);
26858     },
26859
26860
26861     // private
26862     handleMouseDBClick : function(e, t){
26863         
26864     },
26865     // private
26866     handleMouseDown : function(e, t)
26867     {
26868             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26869         if(this.isLocked() || rowIndex < 0 ){
26870             return;
26871         };
26872         if(e.shiftKey && this.last !== false){
26873             var last = this.last;
26874             this.selectRange(last, rowIndex, e.ctrlKey);
26875             this.last = last; // reset the last
26876             t.focus();
26877     
26878         }else{
26879             var isSelected = this.isSelected(rowIndex);
26880             //Roo.log("select row:" + rowIndex);
26881             if(isSelected){
26882                 this.deselectRow(rowIndex);
26883             } else {
26884                         this.selectRow(rowIndex, true);
26885             }
26886     
26887             /*
26888                 if(e.button !== 0 && isSelected){
26889                 alert('rowIndex 2: ' + rowIndex);
26890                     view.focusRow(rowIndex);
26891                 }else if(e.ctrlKey && isSelected){
26892                     this.deselectRow(rowIndex);
26893                 }else if(!isSelected){
26894                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26895                     view.focusRow(rowIndex);
26896                 }
26897             */
26898         }
26899         this.fireEvent("afterselectionchange", this);
26900     },
26901     // private
26902     handleDragableRowClick :  function(grid, rowIndex, e) 
26903     {
26904         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26905             this.selectRow(rowIndex, false);
26906             grid.view.focusRow(rowIndex);
26907              this.fireEvent("afterselectionchange", this);
26908         }
26909     },
26910     
26911     /**
26912      * Selects multiple rows.
26913      * @param {Array} rows Array of the indexes of the row to select
26914      * @param {Boolean} keepExisting (optional) True to keep existing selections
26915      */
26916     selectRows : function(rows, keepExisting){
26917         if(!keepExisting){
26918             this.clearSelections();
26919         }
26920         for(var i = 0, len = rows.length; i < len; i++){
26921             this.selectRow(rows[i], true);
26922         }
26923     },
26924
26925     /**
26926      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26927      * @param {Number} startRow The index of the first row in the range
26928      * @param {Number} endRow The index of the last row in the range
26929      * @param {Boolean} keepExisting (optional) True to retain existing selections
26930      */
26931     selectRange : function(startRow, endRow, keepExisting){
26932         if(this.locked) {
26933             return;
26934         }
26935         if(!keepExisting){
26936             this.clearSelections();
26937         }
26938         if(startRow <= endRow){
26939             for(var i = startRow; i <= endRow; i++){
26940                 this.selectRow(i, true);
26941             }
26942         }else{
26943             for(var i = startRow; i >= endRow; i--){
26944                 this.selectRow(i, true);
26945             }
26946         }
26947     },
26948
26949     /**
26950      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26951      * @param {Number} startRow The index of the first row in the range
26952      * @param {Number} endRow The index of the last row in the range
26953      */
26954     deselectRange : function(startRow, endRow, preventViewNotify){
26955         if(this.locked) {
26956             return;
26957         }
26958         for(var i = startRow; i <= endRow; i++){
26959             this.deselectRow(i, preventViewNotify);
26960         }
26961     },
26962
26963     /**
26964      * Selects a row.
26965      * @param {Number} row The index of the row to select
26966      * @param {Boolean} keepExisting (optional) True to keep existing selections
26967      */
26968     selectRow : function(index, keepExisting, preventViewNotify)
26969     {
26970             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26971             return;
26972         }
26973         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26974             if(!keepExisting || this.singleSelect){
26975                 this.clearSelections();
26976             }
26977             
26978             var r = this.grid.store.getAt(index);
26979             //console.log('selectRow - record id :' + r.id);
26980             
26981             this.selections.add(r);
26982             this.last = this.lastActive = index;
26983             if(!preventViewNotify){
26984                 var proxy = new Roo.Element(
26985                                 this.grid.getRowDom(index)
26986                 );
26987                 proxy.addClass('bg-info info');
26988             }
26989             this.fireEvent("rowselect", this, index, r);
26990             this.fireEvent("selectionchange", this);
26991         }
26992     },
26993
26994     /**
26995      * Deselects a row.
26996      * @param {Number} row The index of the row to deselect
26997      */
26998     deselectRow : function(index, preventViewNotify)
26999     {
27000         if(this.locked) {
27001             return;
27002         }
27003         if(this.last == index){
27004             this.last = false;
27005         }
27006         if(this.lastActive == index){
27007             this.lastActive = false;
27008         }
27009         
27010         var r = this.grid.store.getAt(index);
27011         if (!r) {
27012             return;
27013         }
27014         
27015         this.selections.remove(r);
27016         //.console.log('deselectRow - record id :' + r.id);
27017         if(!preventViewNotify){
27018         
27019             var proxy = new Roo.Element(
27020                 this.grid.getRowDom(index)
27021             );
27022             proxy.removeClass('bg-info info');
27023         }
27024         this.fireEvent("rowdeselect", this, index);
27025         this.fireEvent("selectionchange", this);
27026     },
27027
27028     // private
27029     restoreLast : function(){
27030         if(this._last){
27031             this.last = this._last;
27032         }
27033     },
27034
27035     // private
27036     acceptsNav : function(row, col, cm){
27037         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27038     },
27039
27040     // private
27041     onEditorKey : function(field, e){
27042         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27043         if(k == e.TAB){
27044             e.stopEvent();
27045             ed.completeEdit();
27046             if(e.shiftKey){
27047                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27048             }else{
27049                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27050             }
27051         }else if(k == e.ENTER && !e.ctrlKey){
27052             e.stopEvent();
27053             ed.completeEdit();
27054             if(e.shiftKey){
27055                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27056             }else{
27057                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27058             }
27059         }else if(k == e.ESC){
27060             ed.cancelEdit();
27061         }
27062         if(newCell){
27063             g.startEditing(newCell[0], newCell[1]);
27064         }
27065     }
27066 });
27067 /*
27068  * Based on:
27069  * Ext JS Library 1.1.1
27070  * Copyright(c) 2006-2007, Ext JS, LLC.
27071  *
27072  * Originally Released Under LGPL - original licence link has changed is not relivant.
27073  *
27074  * Fork - LGPL
27075  * <script type="text/javascript">
27076  */
27077  
27078 /**
27079  * @class Roo.bootstrap.PagingToolbar
27080  * @extends Roo.bootstrap.NavSimplebar
27081  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27082  * @constructor
27083  * Create a new PagingToolbar
27084  * @param {Object} config The config object
27085  * @param {Roo.data.Store} store
27086  */
27087 Roo.bootstrap.PagingToolbar = function(config)
27088 {
27089     // old args format still supported... - xtype is prefered..
27090         // created from xtype...
27091     
27092     this.ds = config.dataSource;
27093     
27094     if (config.store && !this.ds) {
27095         this.store= Roo.factory(config.store, Roo.data);
27096         this.ds = this.store;
27097         this.ds.xmodule = this.xmodule || false;
27098     }
27099     
27100     this.toolbarItems = [];
27101     if (config.items) {
27102         this.toolbarItems = config.items;
27103     }
27104     
27105     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27106     
27107     this.cursor = 0;
27108     
27109     if (this.ds) { 
27110         this.bind(this.ds);
27111     }
27112     
27113     if (Roo.bootstrap.version == 4) {
27114         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27115     } else {
27116         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27117     }
27118     
27119 };
27120
27121 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27122     /**
27123      * @cfg {Roo.data.Store} dataSource
27124      * The underlying data store providing the paged data
27125      */
27126     /**
27127      * @cfg {String/HTMLElement/Element} container
27128      * container The id or element that will contain the toolbar
27129      */
27130     /**
27131      * @cfg {Boolean} displayInfo
27132      * True to display the displayMsg (defaults to false)
27133      */
27134     /**
27135      * @cfg {Number} pageSize
27136      * The number of records to display per page (defaults to 20)
27137      */
27138     pageSize: 20,
27139     /**
27140      * @cfg {String} displayMsg
27141      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27142      */
27143     displayMsg : 'Displaying {0} - {1} of {2}',
27144     /**
27145      * @cfg {String} emptyMsg
27146      * The message to display when no records are found (defaults to "No data to display")
27147      */
27148     emptyMsg : 'No data to display',
27149     /**
27150      * Customizable piece of the default paging text (defaults to "Page")
27151      * @type String
27152      */
27153     beforePageText : "Page",
27154     /**
27155      * Customizable piece of the default paging text (defaults to "of %0")
27156      * @type String
27157      */
27158     afterPageText : "of {0}",
27159     /**
27160      * Customizable piece of the default paging text (defaults to "First Page")
27161      * @type String
27162      */
27163     firstText : "First Page",
27164     /**
27165      * Customizable piece of the default paging text (defaults to "Previous Page")
27166      * @type String
27167      */
27168     prevText : "Previous Page",
27169     /**
27170      * Customizable piece of the default paging text (defaults to "Next Page")
27171      * @type String
27172      */
27173     nextText : "Next Page",
27174     /**
27175      * Customizable piece of the default paging text (defaults to "Last Page")
27176      * @type String
27177      */
27178     lastText : "Last Page",
27179     /**
27180      * Customizable piece of the default paging text (defaults to "Refresh")
27181      * @type String
27182      */
27183     refreshText : "Refresh",
27184
27185     buttons : false,
27186     // private
27187     onRender : function(ct, position) 
27188     {
27189         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27190         this.navgroup.parentId = this.id;
27191         this.navgroup.onRender(this.el, null);
27192         // add the buttons to the navgroup
27193         
27194         if(this.displayInfo){
27195             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27196             this.displayEl = this.el.select('.x-paging-info', true).first();
27197 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27198 //            this.displayEl = navel.el.select('span',true).first();
27199         }
27200         
27201         var _this = this;
27202         
27203         if(this.buttons){
27204             Roo.each(_this.buttons, function(e){ // this might need to use render????
27205                Roo.factory(e).render(_this.el);
27206             });
27207         }
27208             
27209         Roo.each(_this.toolbarItems, function(e) {
27210             _this.navgroup.addItem(e);
27211         });
27212         
27213         
27214         this.first = this.navgroup.addItem({
27215             tooltip: this.firstText,
27216             cls: "prev btn-outline-secondary",
27217             html : ' <i class="fa fa-step-backward"></i>',
27218             disabled: true,
27219             preventDefault: true,
27220             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27221         });
27222         
27223         this.prev =  this.navgroup.addItem({
27224             tooltip: this.prevText,
27225             cls: "prev btn-outline-secondary",
27226             html : ' <i class="fa fa-backward"></i>',
27227             disabled: true,
27228             preventDefault: true,
27229             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27230         });
27231     //this.addSeparator();
27232         
27233         
27234         var field = this.navgroup.addItem( {
27235             tagtype : 'span',
27236             cls : 'x-paging-position  btn-outline-secondary',
27237              disabled: true,
27238             html : this.beforePageText  +
27239                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27240                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27241          } ); //?? escaped?
27242         
27243         this.field = field.el.select('input', true).first();
27244         this.field.on("keydown", this.onPagingKeydown, this);
27245         this.field.on("focus", function(){this.dom.select();});
27246     
27247     
27248         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27249         //this.field.setHeight(18);
27250         //this.addSeparator();
27251         this.next = this.navgroup.addItem({
27252             tooltip: this.nextText,
27253             cls: "next btn-outline-secondary",
27254             html : ' <i class="fa fa-forward"></i>',
27255             disabled: true,
27256             preventDefault: true,
27257             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27258         });
27259         this.last = this.navgroup.addItem({
27260             tooltip: this.lastText,
27261             html : ' <i class="fa fa-step-forward"></i>',
27262             cls: "next btn-outline-secondary",
27263             disabled: true,
27264             preventDefault: true,
27265             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27266         });
27267     //this.addSeparator();
27268         this.loading = this.navgroup.addItem({
27269             tooltip: this.refreshText,
27270             cls: "btn-outline-secondary",
27271             html : ' <i class="fa fa-refresh"></i>',
27272             preventDefault: true,
27273             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27274         });
27275         
27276     },
27277
27278     // private
27279     updateInfo : function(){
27280         if(this.displayEl){
27281             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27282             var msg = count == 0 ?
27283                 this.emptyMsg :
27284                 String.format(
27285                     this.displayMsg,
27286                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27287                 );
27288             this.displayEl.update(msg);
27289         }
27290     },
27291
27292     // private
27293     onLoad : function(ds, r, o)
27294     {
27295         this.cursor = o.params.start ? o.params.start : 0;
27296         
27297         var d = this.getPageData(),
27298             ap = d.activePage,
27299             ps = d.pages;
27300         
27301         
27302         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27303         this.field.dom.value = ap;
27304         this.first.setDisabled(ap == 1);
27305         this.prev.setDisabled(ap == 1);
27306         this.next.setDisabled(ap == ps);
27307         this.last.setDisabled(ap == ps);
27308         this.loading.enable();
27309         this.updateInfo();
27310     },
27311
27312     // private
27313     getPageData : function(){
27314         var total = this.ds.getTotalCount();
27315         return {
27316             total : total,
27317             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27318             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27319         };
27320     },
27321
27322     // private
27323     onLoadError : function(){
27324         this.loading.enable();
27325     },
27326
27327     // private
27328     onPagingKeydown : function(e){
27329         var k = e.getKey();
27330         var d = this.getPageData();
27331         if(k == e.RETURN){
27332             var v = this.field.dom.value, pageNum;
27333             if(!v || isNaN(pageNum = parseInt(v, 10))){
27334                 this.field.dom.value = d.activePage;
27335                 return;
27336             }
27337             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27338             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27339             e.stopEvent();
27340         }
27341         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))
27342         {
27343           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27344           this.field.dom.value = pageNum;
27345           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27346           e.stopEvent();
27347         }
27348         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27349         {
27350           var v = this.field.dom.value, pageNum; 
27351           var increment = (e.shiftKey) ? 10 : 1;
27352           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27353                 increment *= -1;
27354           }
27355           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27356             this.field.dom.value = d.activePage;
27357             return;
27358           }
27359           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27360           {
27361             this.field.dom.value = parseInt(v, 10) + increment;
27362             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27363             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27364           }
27365           e.stopEvent();
27366         }
27367     },
27368
27369     // private
27370     beforeLoad : function(){
27371         if(this.loading){
27372             this.loading.disable();
27373         }
27374     },
27375
27376     // private
27377     onClick : function(which){
27378         
27379         var ds = this.ds;
27380         if (!ds) {
27381             return;
27382         }
27383         
27384         switch(which){
27385             case "first":
27386                 ds.load({params:{start: 0, limit: this.pageSize}});
27387             break;
27388             case "prev":
27389                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27390             break;
27391             case "next":
27392                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27393             break;
27394             case "last":
27395                 var total = ds.getTotalCount();
27396                 var extra = total % this.pageSize;
27397                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27398                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27399             break;
27400             case "refresh":
27401                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27402             break;
27403         }
27404     },
27405
27406     /**
27407      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27408      * @param {Roo.data.Store} store The data store to unbind
27409      */
27410     unbind : function(ds){
27411         ds.un("beforeload", this.beforeLoad, this);
27412         ds.un("load", this.onLoad, this);
27413         ds.un("loadexception", this.onLoadError, this);
27414         ds.un("remove", this.updateInfo, this);
27415         ds.un("add", this.updateInfo, this);
27416         this.ds = undefined;
27417     },
27418
27419     /**
27420      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27421      * @param {Roo.data.Store} store The data store to bind
27422      */
27423     bind : function(ds){
27424         ds.on("beforeload", this.beforeLoad, this);
27425         ds.on("load", this.onLoad, this);
27426         ds.on("loadexception", this.onLoadError, this);
27427         ds.on("remove", this.updateInfo, this);
27428         ds.on("add", this.updateInfo, this);
27429         this.ds = ds;
27430     }
27431 });/*
27432  * - LGPL
27433  *
27434  * element
27435  * 
27436  */
27437
27438 /**
27439  * @class Roo.bootstrap.MessageBar
27440  * @extends Roo.bootstrap.Component
27441  * Bootstrap MessageBar class
27442  * @cfg {String} html contents of the MessageBar
27443  * @cfg {String} weight (info | success | warning | danger) default info
27444  * @cfg {String} beforeClass insert the bar before the given class
27445  * @cfg {Boolean} closable (true | false) default false
27446  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27447  * 
27448  * @constructor
27449  * Create a new Element
27450  * @param {Object} config The config object
27451  */
27452
27453 Roo.bootstrap.MessageBar = function(config){
27454     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27455 };
27456
27457 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27458     
27459     html: '',
27460     weight: 'info',
27461     closable: false,
27462     fixed: false,
27463     beforeClass: 'bootstrap-sticky-wrap',
27464     
27465     getAutoCreate : function(){
27466         
27467         var cfg = {
27468             tag: 'div',
27469             cls: 'alert alert-dismissable alert-' + this.weight,
27470             cn: [
27471                 {
27472                     tag: 'span',
27473                     cls: 'message',
27474                     html: this.html || ''
27475                 }
27476             ]
27477         };
27478         
27479         if(this.fixed){
27480             cfg.cls += ' alert-messages-fixed';
27481         }
27482         
27483         if(this.closable){
27484             cfg.cn.push({
27485                 tag: 'button',
27486                 cls: 'close',
27487                 html: 'x'
27488             });
27489         }
27490         
27491         return cfg;
27492     },
27493     
27494     onRender : function(ct, position)
27495     {
27496         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27497         
27498         if(!this.el){
27499             var cfg = Roo.apply({},  this.getAutoCreate());
27500             cfg.id = Roo.id();
27501             
27502             if (this.cls) {
27503                 cfg.cls += ' ' + this.cls;
27504             }
27505             if (this.style) {
27506                 cfg.style = this.style;
27507             }
27508             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27509             
27510             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27511         }
27512         
27513         this.el.select('>button.close').on('click', this.hide, this);
27514         
27515     },
27516     
27517     show : function()
27518     {
27519         if (!this.rendered) {
27520             this.render();
27521         }
27522         
27523         this.el.show();
27524         
27525         this.fireEvent('show', this);
27526         
27527     },
27528     
27529     hide : function()
27530     {
27531         if (!this.rendered) {
27532             this.render();
27533         }
27534         
27535         this.el.hide();
27536         
27537         this.fireEvent('hide', this);
27538     },
27539     
27540     update : function()
27541     {
27542 //        var e = this.el.dom.firstChild;
27543 //        
27544 //        if(this.closable){
27545 //            e = e.nextSibling;
27546 //        }
27547 //        
27548 //        e.data = this.html || '';
27549
27550         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27551     }
27552    
27553 });
27554
27555  
27556
27557      /*
27558  * - LGPL
27559  *
27560  * Graph
27561  * 
27562  */
27563
27564
27565 /**
27566  * @class Roo.bootstrap.Graph
27567  * @extends Roo.bootstrap.Component
27568  * Bootstrap Graph class
27569 > Prameters
27570  -sm {number} sm 4
27571  -md {number} md 5
27572  @cfg {String} graphtype  bar | vbar | pie
27573  @cfg {number} g_x coodinator | centre x (pie)
27574  @cfg {number} g_y coodinator | centre y (pie)
27575  @cfg {number} g_r radius (pie)
27576  @cfg {number} g_height height of the chart (respected by all elements in the set)
27577  @cfg {number} g_width width of the chart (respected by all elements in the set)
27578  @cfg {Object} title The title of the chart
27579     
27580  -{Array}  values
27581  -opts (object) options for the chart 
27582      o {
27583      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27584      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27585      o vgutter (number)
27586      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.
27587      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27588      o to
27589      o stretch (boolean)
27590      o }
27591  -opts (object) options for the pie
27592      o{
27593      o cut
27594      o startAngle (number)
27595      o endAngle (number)
27596      } 
27597  *
27598  * @constructor
27599  * Create a new Input
27600  * @param {Object} config The config object
27601  */
27602
27603 Roo.bootstrap.Graph = function(config){
27604     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27605     
27606     this.addEvents({
27607         // img events
27608         /**
27609          * @event click
27610          * The img click event for the img.
27611          * @param {Roo.EventObject} e
27612          */
27613         "click" : true
27614     });
27615 };
27616
27617 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27618     
27619     sm: 4,
27620     md: 5,
27621     graphtype: 'bar',
27622     g_height: 250,
27623     g_width: 400,
27624     g_x: 50,
27625     g_y: 50,
27626     g_r: 30,
27627     opts:{
27628         //g_colors: this.colors,
27629         g_type: 'soft',
27630         g_gutter: '20%'
27631
27632     },
27633     title : false,
27634
27635     getAutoCreate : function(){
27636         
27637         var cfg = {
27638             tag: 'div',
27639             html : null
27640         };
27641         
27642         
27643         return  cfg;
27644     },
27645
27646     onRender : function(ct,position){
27647         
27648         
27649         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27650         
27651         if (typeof(Raphael) == 'undefined') {
27652             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27653             return;
27654         }
27655         
27656         this.raphael = Raphael(this.el.dom);
27657         
27658                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27659                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27660                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27661                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27662                 /*
27663                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27664                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27665                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27666                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27667                 
27668                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27669                 r.barchart(330, 10, 300, 220, data1);
27670                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27671                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27672                 */
27673                 
27674                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27675                 // r.barchart(30, 30, 560, 250,  xdata, {
27676                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27677                 //     axis : "0 0 1 1",
27678                 //     axisxlabels :  xdata
27679                 //     //yvalues : cols,
27680                    
27681                 // });
27682 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27683 //        
27684 //        this.load(null,xdata,{
27685 //                axis : "0 0 1 1",
27686 //                axisxlabels :  xdata
27687 //                });
27688
27689     },
27690
27691     load : function(graphtype,xdata,opts)
27692     {
27693         this.raphael.clear();
27694         if(!graphtype) {
27695             graphtype = this.graphtype;
27696         }
27697         if(!opts){
27698             opts = this.opts;
27699         }
27700         var r = this.raphael,
27701             fin = function () {
27702                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27703             },
27704             fout = function () {
27705                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27706             },
27707             pfin = function() {
27708                 this.sector.stop();
27709                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27710
27711                 if (this.label) {
27712                     this.label[0].stop();
27713                     this.label[0].attr({ r: 7.5 });
27714                     this.label[1].attr({ "font-weight": 800 });
27715                 }
27716             },
27717             pfout = function() {
27718                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27719
27720                 if (this.label) {
27721                     this.label[0].animate({ r: 5 }, 500, "bounce");
27722                     this.label[1].attr({ "font-weight": 400 });
27723                 }
27724             };
27725
27726         switch(graphtype){
27727             case 'bar':
27728                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27729                 break;
27730             case 'hbar':
27731                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27732                 break;
27733             case 'pie':
27734 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27735 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27736 //            
27737                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27738                 
27739                 break;
27740
27741         }
27742         
27743         if(this.title){
27744             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27745         }
27746         
27747     },
27748     
27749     setTitle: function(o)
27750     {
27751         this.title = o;
27752     },
27753     
27754     initEvents: function() {
27755         
27756         if(!this.href){
27757             this.el.on('click', this.onClick, this);
27758         }
27759     },
27760     
27761     onClick : function(e)
27762     {
27763         Roo.log('img onclick');
27764         this.fireEvent('click', this, e);
27765     }
27766    
27767 });
27768
27769  
27770 /*
27771  * - LGPL
27772  *
27773  * numberBox
27774  * 
27775  */
27776 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27777
27778 /**
27779  * @class Roo.bootstrap.dash.NumberBox
27780  * @extends Roo.bootstrap.Component
27781  * Bootstrap NumberBox class
27782  * @cfg {String} headline Box headline
27783  * @cfg {String} content Box content
27784  * @cfg {String} icon Box icon
27785  * @cfg {String} footer Footer text
27786  * @cfg {String} fhref Footer href
27787  * 
27788  * @constructor
27789  * Create a new NumberBox
27790  * @param {Object} config The config object
27791  */
27792
27793
27794 Roo.bootstrap.dash.NumberBox = function(config){
27795     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27796     
27797 };
27798
27799 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27800     
27801     headline : '',
27802     content : '',
27803     icon : '',
27804     footer : '',
27805     fhref : '',
27806     ficon : '',
27807     
27808     getAutoCreate : function(){
27809         
27810         var cfg = {
27811             tag : 'div',
27812             cls : 'small-box ',
27813             cn : [
27814                 {
27815                     tag : 'div',
27816                     cls : 'inner',
27817                     cn :[
27818                         {
27819                             tag : 'h3',
27820                             cls : 'roo-headline',
27821                             html : this.headline
27822                         },
27823                         {
27824                             tag : 'p',
27825                             cls : 'roo-content',
27826                             html : this.content
27827                         }
27828                     ]
27829                 }
27830             ]
27831         };
27832         
27833         if(this.icon){
27834             cfg.cn.push({
27835                 tag : 'div',
27836                 cls : 'icon',
27837                 cn :[
27838                     {
27839                         tag : 'i',
27840                         cls : 'ion ' + this.icon
27841                     }
27842                 ]
27843             });
27844         }
27845         
27846         if(this.footer){
27847             var footer = {
27848                 tag : 'a',
27849                 cls : 'small-box-footer',
27850                 href : this.fhref || '#',
27851                 html : this.footer
27852             };
27853             
27854             cfg.cn.push(footer);
27855             
27856         }
27857         
27858         return  cfg;
27859     },
27860
27861     onRender : function(ct,position){
27862         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27863
27864
27865        
27866                 
27867     },
27868
27869     setHeadline: function (value)
27870     {
27871         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27872     },
27873     
27874     setFooter: function (value, href)
27875     {
27876         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27877         
27878         if(href){
27879             this.el.select('a.small-box-footer',true).first().attr('href', href);
27880         }
27881         
27882     },
27883
27884     setContent: function (value)
27885     {
27886         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27887     },
27888
27889     initEvents: function() 
27890     {   
27891         
27892     }
27893     
27894 });
27895
27896  
27897 /*
27898  * - LGPL
27899  *
27900  * TabBox
27901  * 
27902  */
27903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27904
27905 /**
27906  * @class Roo.bootstrap.dash.TabBox
27907  * @extends Roo.bootstrap.Component
27908  * Bootstrap TabBox class
27909  * @cfg {String} title Title of the TabBox
27910  * @cfg {String} icon Icon of the TabBox
27911  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27912  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27913  * 
27914  * @constructor
27915  * Create a new TabBox
27916  * @param {Object} config The config object
27917  */
27918
27919
27920 Roo.bootstrap.dash.TabBox = function(config){
27921     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27922     this.addEvents({
27923         // raw events
27924         /**
27925          * @event addpane
27926          * When a pane is added
27927          * @param {Roo.bootstrap.dash.TabPane} pane
27928          */
27929         "addpane" : true,
27930         /**
27931          * @event activatepane
27932          * When a pane is activated
27933          * @param {Roo.bootstrap.dash.TabPane} pane
27934          */
27935         "activatepane" : true
27936         
27937          
27938     });
27939     
27940     this.panes = [];
27941 };
27942
27943 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27944
27945     title : '',
27946     icon : false,
27947     showtabs : true,
27948     tabScrollable : false,
27949     
27950     getChildContainer : function()
27951     {
27952         return this.el.select('.tab-content', true).first();
27953     },
27954     
27955     getAutoCreate : function(){
27956         
27957         var header = {
27958             tag: 'li',
27959             cls: 'pull-left header',
27960             html: this.title,
27961             cn : []
27962         };
27963         
27964         if(this.icon){
27965             header.cn.push({
27966                 tag: 'i',
27967                 cls: 'fa ' + this.icon
27968             });
27969         }
27970         
27971         var h = {
27972             tag: 'ul',
27973             cls: 'nav nav-tabs pull-right',
27974             cn: [
27975                 header
27976             ]
27977         };
27978         
27979         if(this.tabScrollable){
27980             h = {
27981                 tag: 'div',
27982                 cls: 'tab-header',
27983                 cn: [
27984                     {
27985                         tag: 'ul',
27986                         cls: 'nav nav-tabs pull-right',
27987                         cn: [
27988                             header
27989                         ]
27990                     }
27991                 ]
27992             };
27993         }
27994         
27995         var cfg = {
27996             tag: 'div',
27997             cls: 'nav-tabs-custom',
27998             cn: [
27999                 h,
28000                 {
28001                     tag: 'div',
28002                     cls: 'tab-content no-padding',
28003                     cn: []
28004                 }
28005             ]
28006         };
28007
28008         return  cfg;
28009     },
28010     initEvents : function()
28011     {
28012         //Roo.log('add add pane handler');
28013         this.on('addpane', this.onAddPane, this);
28014     },
28015      /**
28016      * Updates the box title
28017      * @param {String} html to set the title to.
28018      */
28019     setTitle : function(value)
28020     {
28021         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28022     },
28023     onAddPane : function(pane)
28024     {
28025         this.panes.push(pane);
28026         //Roo.log('addpane');
28027         //Roo.log(pane);
28028         // tabs are rendere left to right..
28029         if(!this.showtabs){
28030             return;
28031         }
28032         
28033         var ctr = this.el.select('.nav-tabs', true).first();
28034          
28035          
28036         var existing = ctr.select('.nav-tab',true);
28037         var qty = existing.getCount();;
28038         
28039         
28040         var tab = ctr.createChild({
28041             tag : 'li',
28042             cls : 'nav-tab' + (qty ? '' : ' active'),
28043             cn : [
28044                 {
28045                     tag : 'a',
28046                     href:'#',
28047                     html : pane.title
28048                 }
28049             ]
28050         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28051         pane.tab = tab;
28052         
28053         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28054         if (!qty) {
28055             pane.el.addClass('active');
28056         }
28057         
28058                 
28059     },
28060     onTabClick : function(ev,un,ob,pane)
28061     {
28062         //Roo.log('tab - prev default');
28063         ev.preventDefault();
28064         
28065         
28066         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28067         pane.tab.addClass('active');
28068         //Roo.log(pane.title);
28069         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28070         // technically we should have a deactivate event.. but maybe add later.
28071         // and it should not de-activate the selected tab...
28072         this.fireEvent('activatepane', pane);
28073         pane.el.addClass('active');
28074         pane.fireEvent('activate');
28075         
28076         
28077     },
28078     
28079     getActivePane : function()
28080     {
28081         var r = false;
28082         Roo.each(this.panes, function(p) {
28083             if(p.el.hasClass('active')){
28084                 r = p;
28085                 return false;
28086             }
28087             
28088             return;
28089         });
28090         
28091         return r;
28092     }
28093     
28094     
28095 });
28096
28097  
28098 /*
28099  * - LGPL
28100  *
28101  * Tab pane
28102  * 
28103  */
28104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28105 /**
28106  * @class Roo.bootstrap.TabPane
28107  * @extends Roo.bootstrap.Component
28108  * Bootstrap TabPane class
28109  * @cfg {Boolean} active (false | true) Default false
28110  * @cfg {String} title title of panel
28111
28112  * 
28113  * @constructor
28114  * Create a new TabPane
28115  * @param {Object} config The config object
28116  */
28117
28118 Roo.bootstrap.dash.TabPane = function(config){
28119     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28120     
28121     this.addEvents({
28122         // raw events
28123         /**
28124          * @event activate
28125          * When a pane is activated
28126          * @param {Roo.bootstrap.dash.TabPane} pane
28127          */
28128         "activate" : true
28129          
28130     });
28131 };
28132
28133 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28134     
28135     active : false,
28136     title : '',
28137     
28138     // the tabBox that this is attached to.
28139     tab : false,
28140      
28141     getAutoCreate : function() 
28142     {
28143         var cfg = {
28144             tag: 'div',
28145             cls: 'tab-pane'
28146         };
28147         
28148         if(this.active){
28149             cfg.cls += ' active';
28150         }
28151         
28152         return cfg;
28153     },
28154     initEvents  : function()
28155     {
28156         //Roo.log('trigger add pane handler');
28157         this.parent().fireEvent('addpane', this)
28158     },
28159     
28160      /**
28161      * Updates the tab title 
28162      * @param {String} html to set the title to.
28163      */
28164     setTitle: function(str)
28165     {
28166         if (!this.tab) {
28167             return;
28168         }
28169         this.title = str;
28170         this.tab.select('a', true).first().dom.innerHTML = str;
28171         
28172     }
28173     
28174     
28175     
28176 });
28177
28178  
28179
28180
28181  /*
28182  * - LGPL
28183  *
28184  * menu
28185  * 
28186  */
28187 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28188
28189 /**
28190  * @class Roo.bootstrap.menu.Menu
28191  * @extends Roo.bootstrap.Component
28192  * Bootstrap Menu class - container for Menu
28193  * @cfg {String} html Text of the menu
28194  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28195  * @cfg {String} icon Font awesome icon
28196  * @cfg {String} pos Menu align to (top | bottom) default bottom
28197  * 
28198  * 
28199  * @constructor
28200  * Create a new Menu
28201  * @param {Object} config The config object
28202  */
28203
28204
28205 Roo.bootstrap.menu.Menu = function(config){
28206     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28207     
28208     this.addEvents({
28209         /**
28210          * @event beforeshow
28211          * Fires before this menu is displayed
28212          * @param {Roo.bootstrap.menu.Menu} this
28213          */
28214         beforeshow : true,
28215         /**
28216          * @event beforehide
28217          * Fires before this menu is hidden
28218          * @param {Roo.bootstrap.menu.Menu} this
28219          */
28220         beforehide : true,
28221         /**
28222          * @event show
28223          * Fires after this menu is displayed
28224          * @param {Roo.bootstrap.menu.Menu} this
28225          */
28226         show : true,
28227         /**
28228          * @event hide
28229          * Fires after this menu is hidden
28230          * @param {Roo.bootstrap.menu.Menu} this
28231          */
28232         hide : true,
28233         /**
28234          * @event click
28235          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28236          * @param {Roo.bootstrap.menu.Menu} this
28237          * @param {Roo.EventObject} e
28238          */
28239         click : true
28240     });
28241     
28242 };
28243
28244 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28245     
28246     submenu : false,
28247     html : '',
28248     weight : 'default',
28249     icon : false,
28250     pos : 'bottom',
28251     
28252     
28253     getChildContainer : function() {
28254         if(this.isSubMenu){
28255             return this.el;
28256         }
28257         
28258         return this.el.select('ul.dropdown-menu', true).first();  
28259     },
28260     
28261     getAutoCreate : function()
28262     {
28263         var text = [
28264             {
28265                 tag : 'span',
28266                 cls : 'roo-menu-text',
28267                 html : this.html
28268             }
28269         ];
28270         
28271         if(this.icon){
28272             text.unshift({
28273                 tag : 'i',
28274                 cls : 'fa ' + this.icon
28275             })
28276         }
28277         
28278         
28279         var cfg = {
28280             tag : 'div',
28281             cls : 'btn-group',
28282             cn : [
28283                 {
28284                     tag : 'button',
28285                     cls : 'dropdown-button btn btn-' + this.weight,
28286                     cn : text
28287                 },
28288                 {
28289                     tag : 'button',
28290                     cls : 'dropdown-toggle btn btn-' + this.weight,
28291                     cn : [
28292                         {
28293                             tag : 'span',
28294                             cls : 'caret'
28295                         }
28296                     ]
28297                 },
28298                 {
28299                     tag : 'ul',
28300                     cls : 'dropdown-menu'
28301                 }
28302             ]
28303             
28304         };
28305         
28306         if(this.pos == 'top'){
28307             cfg.cls += ' dropup';
28308         }
28309         
28310         if(this.isSubMenu){
28311             cfg = {
28312                 tag : 'ul',
28313                 cls : 'dropdown-menu'
28314             }
28315         }
28316         
28317         return cfg;
28318     },
28319     
28320     onRender : function(ct, position)
28321     {
28322         this.isSubMenu = ct.hasClass('dropdown-submenu');
28323         
28324         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28325     },
28326     
28327     initEvents : function() 
28328     {
28329         if(this.isSubMenu){
28330             return;
28331         }
28332         
28333         this.hidden = true;
28334         
28335         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28336         this.triggerEl.on('click', this.onTriggerPress, this);
28337         
28338         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28339         this.buttonEl.on('click', this.onClick, this);
28340         
28341     },
28342     
28343     list : function()
28344     {
28345         if(this.isSubMenu){
28346             return this.el;
28347         }
28348         
28349         return this.el.select('ul.dropdown-menu', true).first();
28350     },
28351     
28352     onClick : function(e)
28353     {
28354         this.fireEvent("click", this, e);
28355     },
28356     
28357     onTriggerPress  : function(e)
28358     {   
28359         if (this.isVisible()) {
28360             this.hide();
28361         } else {
28362             this.show();
28363         }
28364     },
28365     
28366     isVisible : function(){
28367         return !this.hidden;
28368     },
28369     
28370     show : function()
28371     {
28372         this.fireEvent("beforeshow", this);
28373         
28374         this.hidden = false;
28375         this.el.addClass('open');
28376         
28377         Roo.get(document).on("mouseup", this.onMouseUp, this);
28378         
28379         this.fireEvent("show", this);
28380         
28381         
28382     },
28383     
28384     hide : function()
28385     {
28386         this.fireEvent("beforehide", this);
28387         
28388         this.hidden = true;
28389         this.el.removeClass('open');
28390         
28391         Roo.get(document).un("mouseup", this.onMouseUp);
28392         
28393         this.fireEvent("hide", this);
28394     },
28395     
28396     onMouseUp : function()
28397     {
28398         this.hide();
28399     }
28400     
28401 });
28402
28403  
28404  /*
28405  * - LGPL
28406  *
28407  * menu item
28408  * 
28409  */
28410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28411
28412 /**
28413  * @class Roo.bootstrap.menu.Item
28414  * @extends Roo.bootstrap.Component
28415  * Bootstrap MenuItem class
28416  * @cfg {Boolean} submenu (true | false) default false
28417  * @cfg {String} html text of the item
28418  * @cfg {String} href the link
28419  * @cfg {Boolean} disable (true | false) default false
28420  * @cfg {Boolean} preventDefault (true | false) default true
28421  * @cfg {String} icon Font awesome icon
28422  * @cfg {String} pos Submenu align to (left | right) default right 
28423  * 
28424  * 
28425  * @constructor
28426  * Create a new Item
28427  * @param {Object} config The config object
28428  */
28429
28430
28431 Roo.bootstrap.menu.Item = function(config){
28432     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28433     this.addEvents({
28434         /**
28435          * @event mouseover
28436          * Fires when the mouse is hovering over this menu
28437          * @param {Roo.bootstrap.menu.Item} this
28438          * @param {Roo.EventObject} e
28439          */
28440         mouseover : true,
28441         /**
28442          * @event mouseout
28443          * Fires when the mouse exits this menu
28444          * @param {Roo.bootstrap.menu.Item} this
28445          * @param {Roo.EventObject} e
28446          */
28447         mouseout : true,
28448         // raw events
28449         /**
28450          * @event click
28451          * The raw click event for the entire grid.
28452          * @param {Roo.EventObject} e
28453          */
28454         click : true
28455     });
28456 };
28457
28458 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28459     
28460     submenu : false,
28461     href : '',
28462     html : '',
28463     preventDefault: true,
28464     disable : false,
28465     icon : false,
28466     pos : 'right',
28467     
28468     getAutoCreate : function()
28469     {
28470         var text = [
28471             {
28472                 tag : 'span',
28473                 cls : 'roo-menu-item-text',
28474                 html : this.html
28475             }
28476         ];
28477         
28478         if(this.icon){
28479             text.unshift({
28480                 tag : 'i',
28481                 cls : 'fa ' + this.icon
28482             })
28483         }
28484         
28485         var cfg = {
28486             tag : 'li',
28487             cn : [
28488                 {
28489                     tag : 'a',
28490                     href : this.href || '#',
28491                     cn : text
28492                 }
28493             ]
28494         };
28495         
28496         if(this.disable){
28497             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28498         }
28499         
28500         if(this.submenu){
28501             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28502             
28503             if(this.pos == 'left'){
28504                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28505             }
28506         }
28507         
28508         return cfg;
28509     },
28510     
28511     initEvents : function() 
28512     {
28513         this.el.on('mouseover', this.onMouseOver, this);
28514         this.el.on('mouseout', this.onMouseOut, this);
28515         
28516         this.el.select('a', true).first().on('click', this.onClick, this);
28517         
28518     },
28519     
28520     onClick : function(e)
28521     {
28522         if(this.preventDefault){
28523             e.preventDefault();
28524         }
28525         
28526         this.fireEvent("click", this, e);
28527     },
28528     
28529     onMouseOver : function(e)
28530     {
28531         if(this.submenu && this.pos == 'left'){
28532             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28533         }
28534         
28535         this.fireEvent("mouseover", this, e);
28536     },
28537     
28538     onMouseOut : function(e)
28539     {
28540         this.fireEvent("mouseout", this, e);
28541     }
28542 });
28543
28544  
28545
28546  /*
28547  * - LGPL
28548  *
28549  * menu separator
28550  * 
28551  */
28552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28553
28554 /**
28555  * @class Roo.bootstrap.menu.Separator
28556  * @extends Roo.bootstrap.Component
28557  * Bootstrap Separator class
28558  * 
28559  * @constructor
28560  * Create a new Separator
28561  * @param {Object} config The config object
28562  */
28563
28564
28565 Roo.bootstrap.menu.Separator = function(config){
28566     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28567 };
28568
28569 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28570     
28571     getAutoCreate : function(){
28572         var cfg = {
28573             tag : 'li',
28574             cls: 'divider'
28575         };
28576         
28577         return cfg;
28578     }
28579    
28580 });
28581
28582  
28583
28584  /*
28585  * - LGPL
28586  *
28587  * Tooltip
28588  * 
28589  */
28590
28591 /**
28592  * @class Roo.bootstrap.Tooltip
28593  * Bootstrap Tooltip class
28594  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28595  * to determine which dom element triggers the tooltip.
28596  * 
28597  * It needs to add support for additional attributes like tooltip-position
28598  * 
28599  * @constructor
28600  * Create a new Toolti
28601  * @param {Object} config The config object
28602  */
28603
28604 Roo.bootstrap.Tooltip = function(config){
28605     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28606     
28607     this.alignment = Roo.bootstrap.Tooltip.alignment;
28608     
28609     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28610         this.alignment = config.alignment;
28611     }
28612     
28613 };
28614
28615 Roo.apply(Roo.bootstrap.Tooltip, {
28616     /**
28617      * @function init initialize tooltip monitoring.
28618      * @static
28619      */
28620     currentEl : false,
28621     currentTip : false,
28622     currentRegion : false,
28623     
28624     //  init : delay?
28625     
28626     init : function()
28627     {
28628         Roo.get(document).on('mouseover', this.enter ,this);
28629         Roo.get(document).on('mouseout', this.leave, this);
28630          
28631         
28632         this.currentTip = new Roo.bootstrap.Tooltip();
28633     },
28634     
28635     enter : function(ev)
28636     {
28637         var dom = ev.getTarget();
28638         
28639         //Roo.log(['enter',dom]);
28640         var el = Roo.fly(dom);
28641         if (this.currentEl) {
28642             //Roo.log(dom);
28643             //Roo.log(this.currentEl);
28644             //Roo.log(this.currentEl.contains(dom));
28645             if (this.currentEl == el) {
28646                 return;
28647             }
28648             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28649                 return;
28650             }
28651
28652         }
28653         
28654         if (this.currentTip.el) {
28655             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28656         }    
28657         //Roo.log(ev);
28658         
28659         if(!el || el.dom == document){
28660             return;
28661         }
28662         
28663         var bindEl = el;
28664         
28665         // you can not look for children, as if el is the body.. then everythign is the child..
28666         if (!el.attr('tooltip')) { //
28667             if (!el.select("[tooltip]").elements.length) {
28668                 return;
28669             }
28670             // is the mouse over this child...?
28671             bindEl = el.select("[tooltip]").first();
28672             var xy = ev.getXY();
28673             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28674                 //Roo.log("not in region.");
28675                 return;
28676             }
28677             //Roo.log("child element over..");
28678             
28679         }
28680         this.currentEl = bindEl;
28681         this.currentTip.bind(bindEl);
28682         this.currentRegion = Roo.lib.Region.getRegion(dom);
28683         this.currentTip.enter();
28684         
28685     },
28686     leave : function(ev)
28687     {
28688         var dom = ev.getTarget();
28689         //Roo.log(['leave',dom]);
28690         if (!this.currentEl) {
28691             return;
28692         }
28693         
28694         
28695         if (dom != this.currentEl.dom) {
28696             return;
28697         }
28698         var xy = ev.getXY();
28699         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28700             return;
28701         }
28702         // only activate leave if mouse cursor is outside... bounding box..
28703         
28704         
28705         
28706         
28707         if (this.currentTip) {
28708             this.currentTip.leave();
28709         }
28710         //Roo.log('clear currentEl');
28711         this.currentEl = false;
28712         
28713         
28714     },
28715     alignment : {
28716         'left' : ['r-l', [-2,0], 'right'],
28717         'right' : ['l-r', [2,0], 'left'],
28718         'bottom' : ['t-b', [0,2], 'top'],
28719         'top' : [ 'b-t', [0,-2], 'bottom']
28720     }
28721     
28722 });
28723
28724
28725 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28726     
28727     
28728     bindEl : false,
28729     
28730     delay : null, // can be { show : 300 , hide: 500}
28731     
28732     timeout : null,
28733     
28734     hoverState : null, //???
28735     
28736     placement : 'bottom', 
28737     
28738     alignment : false,
28739     
28740     getAutoCreate : function(){
28741     
28742         var cfg = {
28743            cls : 'tooltip',   
28744            role : 'tooltip',
28745            cn : [
28746                 {
28747                     cls : 'tooltip-arrow arrow'
28748                 },
28749                 {
28750                     cls : 'tooltip-inner'
28751                 }
28752            ]
28753         };
28754         
28755         return cfg;
28756     },
28757     bind : function(el)
28758     {
28759         this.bindEl = el;
28760     },
28761     
28762     initEvents : function()
28763     {
28764         this.arrowEl = this.el.select('.arrow', true).first();
28765         this.innerEl = this.el.select('.tooltip-inner', true).first();
28766     },
28767     
28768     enter : function () {
28769        
28770         if (this.timeout != null) {
28771             clearTimeout(this.timeout);
28772         }
28773         
28774         this.hoverState = 'in';
28775          //Roo.log("enter - show");
28776         if (!this.delay || !this.delay.show) {
28777             this.show();
28778             return;
28779         }
28780         var _t = this;
28781         this.timeout = setTimeout(function () {
28782             if (_t.hoverState == 'in') {
28783                 _t.show();
28784             }
28785         }, this.delay.show);
28786     },
28787     leave : function()
28788     {
28789         clearTimeout(this.timeout);
28790     
28791         this.hoverState = 'out';
28792          if (!this.delay || !this.delay.hide) {
28793             this.hide();
28794             return;
28795         }
28796        
28797         var _t = this;
28798         this.timeout = setTimeout(function () {
28799             //Roo.log("leave - timeout");
28800             
28801             if (_t.hoverState == 'out') {
28802                 _t.hide();
28803                 Roo.bootstrap.Tooltip.currentEl = false;
28804             }
28805         }, delay);
28806     },
28807     
28808     show : function (msg)
28809     {
28810         if (!this.el) {
28811             this.render(document.body);
28812         }
28813         // set content.
28814         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28815         
28816         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28817         
28818         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28819         
28820         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28821                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28822         
28823         var placement = typeof this.placement == 'function' ?
28824             this.placement.call(this, this.el, on_el) :
28825             this.placement;
28826             
28827         var autoToken = /\s?auto?\s?/i;
28828         var autoPlace = autoToken.test(placement);
28829         if (autoPlace) {
28830             placement = placement.replace(autoToken, '') || 'top';
28831         }
28832         
28833         //this.el.detach()
28834         //this.el.setXY([0,0]);
28835         this.el.show();
28836         //this.el.dom.style.display='block';
28837         
28838         //this.el.appendTo(on_el);
28839         
28840         var p = this.getPosition();
28841         var box = this.el.getBox();
28842         
28843         if (autoPlace) {
28844             // fixme..
28845         }
28846         
28847         var align = this.alignment[placement];
28848         
28849         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28850         
28851         if(placement == 'top' || placement == 'bottom'){
28852             if(xy[0] < 0){
28853                 placement = 'right';
28854             }
28855             
28856             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28857                 placement = 'left';
28858             }
28859             
28860             var scroll = Roo.select('body', true).first().getScroll();
28861             
28862             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28863                 placement = 'top';
28864             }
28865             
28866             align = this.alignment[placement];
28867             
28868             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28869             
28870         }
28871         
28872         this.el.alignTo(this.bindEl, align[0],align[1]);
28873         //var arrow = this.el.select('.arrow',true).first();
28874         //arrow.set(align[2], 
28875         
28876         this.el.addClass(placement);
28877         this.el.addClass("bs-tooltip-"+ placement);
28878         
28879         this.el.addClass('in fade show');
28880         
28881         this.hoverState = null;
28882         
28883         if (this.el.hasClass('fade')) {
28884             // fade it?
28885         }
28886         
28887         
28888         
28889         
28890         
28891     },
28892     hide : function()
28893     {
28894          
28895         if (!this.el) {
28896             return;
28897         }
28898         //this.el.setXY([0,0]);
28899         this.el.removeClass(['show', 'in']);
28900         //this.el.hide();
28901         
28902     }
28903     
28904 });
28905  
28906
28907  /*
28908  * - LGPL
28909  *
28910  * Location Picker
28911  * 
28912  */
28913
28914 /**
28915  * @class Roo.bootstrap.LocationPicker
28916  * @extends Roo.bootstrap.Component
28917  * Bootstrap LocationPicker class
28918  * @cfg {Number} latitude Position when init default 0
28919  * @cfg {Number} longitude Position when init default 0
28920  * @cfg {Number} zoom default 15
28921  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28922  * @cfg {Boolean} mapTypeControl default false
28923  * @cfg {Boolean} disableDoubleClickZoom default false
28924  * @cfg {Boolean} scrollwheel default true
28925  * @cfg {Boolean} streetViewControl default false
28926  * @cfg {Number} radius default 0
28927  * @cfg {String} locationName
28928  * @cfg {Boolean} draggable default true
28929  * @cfg {Boolean} enableAutocomplete default false
28930  * @cfg {Boolean} enableReverseGeocode default true
28931  * @cfg {String} markerTitle
28932  * 
28933  * @constructor
28934  * Create a new LocationPicker
28935  * @param {Object} config The config object
28936  */
28937
28938
28939 Roo.bootstrap.LocationPicker = function(config){
28940     
28941     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28942     
28943     this.addEvents({
28944         /**
28945          * @event initial
28946          * Fires when the picker initialized.
28947          * @param {Roo.bootstrap.LocationPicker} this
28948          * @param {Google Location} location
28949          */
28950         initial : true,
28951         /**
28952          * @event positionchanged
28953          * Fires when the picker position changed.
28954          * @param {Roo.bootstrap.LocationPicker} this
28955          * @param {Google Location} location
28956          */
28957         positionchanged : true,
28958         /**
28959          * @event resize
28960          * Fires when the map resize.
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          */
28963         resize : true,
28964         /**
28965          * @event show
28966          * Fires when the map show.
28967          * @param {Roo.bootstrap.LocationPicker} this
28968          */
28969         show : true,
28970         /**
28971          * @event hide
28972          * Fires when the map hide.
28973          * @param {Roo.bootstrap.LocationPicker} this
28974          */
28975         hide : true,
28976         /**
28977          * @event mapClick
28978          * Fires when click the map.
28979          * @param {Roo.bootstrap.LocationPicker} this
28980          * @param {Map event} e
28981          */
28982         mapClick : true,
28983         /**
28984          * @event mapRightClick
28985          * Fires when right click the map.
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          * @param {Map event} e
28988          */
28989         mapRightClick : true,
28990         /**
28991          * @event markerClick
28992          * Fires when click the marker.
28993          * @param {Roo.bootstrap.LocationPicker} this
28994          * @param {Map event} e
28995          */
28996         markerClick : true,
28997         /**
28998          * @event markerRightClick
28999          * Fires when right click the marker.
29000          * @param {Roo.bootstrap.LocationPicker} this
29001          * @param {Map event} e
29002          */
29003         markerRightClick : true,
29004         /**
29005          * @event OverlayViewDraw
29006          * Fires when OverlayView Draw
29007          * @param {Roo.bootstrap.LocationPicker} this
29008          */
29009         OverlayViewDraw : true,
29010         /**
29011          * @event OverlayViewOnAdd
29012          * Fires when OverlayView Draw
29013          * @param {Roo.bootstrap.LocationPicker} this
29014          */
29015         OverlayViewOnAdd : true,
29016         /**
29017          * @event OverlayViewOnRemove
29018          * Fires when OverlayView Draw
29019          * @param {Roo.bootstrap.LocationPicker} this
29020          */
29021         OverlayViewOnRemove : true,
29022         /**
29023          * @event OverlayViewShow
29024          * Fires when OverlayView Draw
29025          * @param {Roo.bootstrap.LocationPicker} this
29026          * @param {Pixel} cpx
29027          */
29028         OverlayViewShow : true,
29029         /**
29030          * @event OverlayViewHide
29031          * Fires when OverlayView Draw
29032          * @param {Roo.bootstrap.LocationPicker} this
29033          */
29034         OverlayViewHide : true,
29035         /**
29036          * @event loadexception
29037          * Fires when load google lib failed.
29038          * @param {Roo.bootstrap.LocationPicker} this
29039          */
29040         loadexception : true
29041     });
29042         
29043 };
29044
29045 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29046     
29047     gMapContext: false,
29048     
29049     latitude: 0,
29050     longitude: 0,
29051     zoom: 15,
29052     mapTypeId: false,
29053     mapTypeControl: false,
29054     disableDoubleClickZoom: false,
29055     scrollwheel: true,
29056     streetViewControl: false,
29057     radius: 0,
29058     locationName: '',
29059     draggable: true,
29060     enableAutocomplete: false,
29061     enableReverseGeocode: true,
29062     markerTitle: '',
29063     
29064     getAutoCreate: function()
29065     {
29066
29067         var cfg = {
29068             tag: 'div',
29069             cls: 'roo-location-picker'
29070         };
29071         
29072         return cfg
29073     },
29074     
29075     initEvents: function(ct, position)
29076     {       
29077         if(!this.el.getWidth() || this.isApplied()){
29078             return;
29079         }
29080         
29081         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29082         
29083         this.initial();
29084     },
29085     
29086     initial: function()
29087     {
29088         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29089             this.fireEvent('loadexception', this);
29090             return;
29091         }
29092         
29093         if(!this.mapTypeId){
29094             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29095         }
29096         
29097         this.gMapContext = this.GMapContext();
29098         
29099         this.initOverlayView();
29100         
29101         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29102         
29103         var _this = this;
29104                 
29105         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29106             _this.setPosition(_this.gMapContext.marker.position);
29107         });
29108         
29109         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29110             _this.fireEvent('mapClick', this, event);
29111             
29112         });
29113
29114         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29115             _this.fireEvent('mapRightClick', this, event);
29116             
29117         });
29118         
29119         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29120             _this.fireEvent('markerClick', this, event);
29121             
29122         });
29123
29124         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29125             _this.fireEvent('markerRightClick', this, event);
29126             
29127         });
29128         
29129         this.setPosition(this.gMapContext.location);
29130         
29131         this.fireEvent('initial', this, this.gMapContext.location);
29132     },
29133     
29134     initOverlayView: function()
29135     {
29136         var _this = this;
29137         
29138         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29139             
29140             draw: function()
29141             {
29142                 _this.fireEvent('OverlayViewDraw', _this);
29143             },
29144             
29145             onAdd: function()
29146             {
29147                 _this.fireEvent('OverlayViewOnAdd', _this);
29148             },
29149             
29150             onRemove: function()
29151             {
29152                 _this.fireEvent('OverlayViewOnRemove', _this);
29153             },
29154             
29155             show: function(cpx)
29156             {
29157                 _this.fireEvent('OverlayViewShow', _this, cpx);
29158             },
29159             
29160             hide: function()
29161             {
29162                 _this.fireEvent('OverlayViewHide', _this);
29163             }
29164             
29165         });
29166     },
29167     
29168     fromLatLngToContainerPixel: function(event)
29169     {
29170         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29171     },
29172     
29173     isApplied: function() 
29174     {
29175         return this.getGmapContext() == false ? false : true;
29176     },
29177     
29178     getGmapContext: function() 
29179     {
29180         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29181     },
29182     
29183     GMapContext: function() 
29184     {
29185         var position = new google.maps.LatLng(this.latitude, this.longitude);
29186         
29187         var _map = new google.maps.Map(this.el.dom, {
29188             center: position,
29189             zoom: this.zoom,
29190             mapTypeId: this.mapTypeId,
29191             mapTypeControl: this.mapTypeControl,
29192             disableDoubleClickZoom: this.disableDoubleClickZoom,
29193             scrollwheel: this.scrollwheel,
29194             streetViewControl: this.streetViewControl,
29195             locationName: this.locationName,
29196             draggable: this.draggable,
29197             enableAutocomplete: this.enableAutocomplete,
29198             enableReverseGeocode: this.enableReverseGeocode
29199         });
29200         
29201         var _marker = new google.maps.Marker({
29202             position: position,
29203             map: _map,
29204             title: this.markerTitle,
29205             draggable: this.draggable
29206         });
29207         
29208         return {
29209             map: _map,
29210             marker: _marker,
29211             circle: null,
29212             location: position,
29213             radius: this.radius,
29214             locationName: this.locationName,
29215             addressComponents: {
29216                 formatted_address: null,
29217                 addressLine1: null,
29218                 addressLine2: null,
29219                 streetName: null,
29220                 streetNumber: null,
29221                 city: null,
29222                 district: null,
29223                 state: null,
29224                 stateOrProvince: null
29225             },
29226             settings: this,
29227             domContainer: this.el.dom,
29228             geodecoder: new google.maps.Geocoder()
29229         };
29230     },
29231     
29232     drawCircle: function(center, radius, options) 
29233     {
29234         if (this.gMapContext.circle != null) {
29235             this.gMapContext.circle.setMap(null);
29236         }
29237         if (radius > 0) {
29238             radius *= 1;
29239             options = Roo.apply({}, options, {
29240                 strokeColor: "#0000FF",
29241                 strokeOpacity: .35,
29242                 strokeWeight: 2,
29243                 fillColor: "#0000FF",
29244                 fillOpacity: .2
29245             });
29246             
29247             options.map = this.gMapContext.map;
29248             options.radius = radius;
29249             options.center = center;
29250             this.gMapContext.circle = new google.maps.Circle(options);
29251             return this.gMapContext.circle;
29252         }
29253         
29254         return null;
29255     },
29256     
29257     setPosition: function(location) 
29258     {
29259         this.gMapContext.location = location;
29260         this.gMapContext.marker.setPosition(location);
29261         this.gMapContext.map.panTo(location);
29262         this.drawCircle(location, this.gMapContext.radius, {});
29263         
29264         var _this = this;
29265         
29266         if (this.gMapContext.settings.enableReverseGeocode) {
29267             this.gMapContext.geodecoder.geocode({
29268                 latLng: this.gMapContext.location
29269             }, function(results, status) {
29270                 
29271                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29272                     _this.gMapContext.locationName = results[0].formatted_address;
29273                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29274                     
29275                     _this.fireEvent('positionchanged', this, location);
29276                 }
29277             });
29278             
29279             return;
29280         }
29281         
29282         this.fireEvent('positionchanged', this, location);
29283     },
29284     
29285     resize: function()
29286     {
29287         google.maps.event.trigger(this.gMapContext.map, "resize");
29288         
29289         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29290         
29291         this.fireEvent('resize', this);
29292     },
29293     
29294     setPositionByLatLng: function(latitude, longitude)
29295     {
29296         this.setPosition(new google.maps.LatLng(latitude, longitude));
29297     },
29298     
29299     getCurrentPosition: function() 
29300     {
29301         return {
29302             latitude: this.gMapContext.location.lat(),
29303             longitude: this.gMapContext.location.lng()
29304         };
29305     },
29306     
29307     getAddressName: function() 
29308     {
29309         return this.gMapContext.locationName;
29310     },
29311     
29312     getAddressComponents: function() 
29313     {
29314         return this.gMapContext.addressComponents;
29315     },
29316     
29317     address_component_from_google_geocode: function(address_components) 
29318     {
29319         var result = {};
29320         
29321         for (var i = 0; i < address_components.length; i++) {
29322             var component = address_components[i];
29323             if (component.types.indexOf("postal_code") >= 0) {
29324                 result.postalCode = component.short_name;
29325             } else if (component.types.indexOf("street_number") >= 0) {
29326                 result.streetNumber = component.short_name;
29327             } else if (component.types.indexOf("route") >= 0) {
29328                 result.streetName = component.short_name;
29329             } else if (component.types.indexOf("neighborhood") >= 0) {
29330                 result.city = component.short_name;
29331             } else if (component.types.indexOf("locality") >= 0) {
29332                 result.city = component.short_name;
29333             } else if (component.types.indexOf("sublocality") >= 0) {
29334                 result.district = component.short_name;
29335             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29336                 result.stateOrProvince = component.short_name;
29337             } else if (component.types.indexOf("country") >= 0) {
29338                 result.country = component.short_name;
29339             }
29340         }
29341         
29342         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29343         result.addressLine2 = "";
29344         return result;
29345     },
29346     
29347     setZoomLevel: function(zoom)
29348     {
29349         this.gMapContext.map.setZoom(zoom);
29350     },
29351     
29352     show: function()
29353     {
29354         if(!this.el){
29355             return;
29356         }
29357         
29358         this.el.show();
29359         
29360         this.resize();
29361         
29362         this.fireEvent('show', this);
29363     },
29364     
29365     hide: function()
29366     {
29367         if(!this.el){
29368             return;
29369         }
29370         
29371         this.el.hide();
29372         
29373         this.fireEvent('hide', this);
29374     }
29375     
29376 });
29377
29378 Roo.apply(Roo.bootstrap.LocationPicker, {
29379     
29380     OverlayView : function(map, options)
29381     {
29382         options = options || {};
29383         
29384         this.setMap(map);
29385     }
29386     
29387     
29388 });/**
29389  * @class Roo.bootstrap.Alert
29390  * @extends Roo.bootstrap.Component
29391  * Bootstrap Alert class - shows an alert area box
29392  * eg
29393  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29394   Enter a valid email address
29395 </div>
29396  * @licence LGPL
29397  * @cfg {String} title The title of alert
29398  * @cfg {String} html The content of alert
29399  * @cfg {String} weight (  success | info | warning | danger )
29400  * @cfg {String} faicon font-awesomeicon
29401  * 
29402  * @constructor
29403  * Create a new alert
29404  * @param {Object} config The config object
29405  */
29406
29407
29408 Roo.bootstrap.Alert = function(config){
29409     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29410     
29411 };
29412
29413 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29414     
29415     title: '',
29416     html: '',
29417     weight: false,
29418     faicon: false,
29419     
29420     getAutoCreate : function()
29421     {
29422         
29423         var cfg = {
29424             tag : 'div',
29425             cls : 'alert',
29426             cn : [
29427                 {
29428                     tag : 'i',
29429                     cls : 'roo-alert-icon'
29430                     
29431                 },
29432                 {
29433                     tag : 'b',
29434                     cls : 'roo-alert-title',
29435                     html : this.title
29436                 },
29437                 {
29438                     tag : 'span',
29439                     cls : 'roo-alert-text',
29440                     html : this.html
29441                 }
29442             ]
29443         };
29444         
29445         if(this.faicon){
29446             cfg.cn[0].cls += ' fa ' + this.faicon;
29447         }
29448         
29449         if(this.weight){
29450             cfg.cls += ' alert-' + this.weight;
29451         }
29452         
29453         return cfg;
29454     },
29455     
29456     initEvents: function() 
29457     {
29458         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29459     },
29460     
29461     setTitle : function(str)
29462     {
29463         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29464     },
29465     
29466     setText : function(str)
29467     {
29468         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29469     },
29470     
29471     setWeight : function(weight)
29472     {
29473         if(this.weight){
29474             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29475         }
29476         
29477         this.weight = weight;
29478         
29479         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29480     },
29481     
29482     setIcon : function(icon)
29483     {
29484         if(this.faicon){
29485             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29486         }
29487         
29488         this.faicon = icon;
29489         
29490         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29491     },
29492     
29493     hide: function() 
29494     {
29495         this.el.hide();   
29496     },
29497     
29498     show: function() 
29499     {  
29500         this.el.show();   
29501     }
29502     
29503 });
29504
29505  
29506 /*
29507 * Licence: LGPL
29508 */
29509
29510 /**
29511  * @class Roo.bootstrap.UploadCropbox
29512  * @extends Roo.bootstrap.Component
29513  * Bootstrap UploadCropbox class
29514  * @cfg {String} emptyText show when image has been loaded
29515  * @cfg {String} rotateNotify show when image too small to rotate
29516  * @cfg {Number} errorTimeout default 3000
29517  * @cfg {Number} minWidth default 300
29518  * @cfg {Number} minHeight default 300
29519  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29520  * @cfg {Boolean} isDocument (true|false) default false
29521  * @cfg {String} url action url
29522  * @cfg {String} paramName default 'imageUpload'
29523  * @cfg {String} method default POST
29524  * @cfg {Boolean} loadMask (true|false) default true
29525  * @cfg {Boolean} loadingText default 'Loading...'
29526  * 
29527  * @constructor
29528  * Create a new UploadCropbox
29529  * @param {Object} config The config object
29530  */
29531
29532 Roo.bootstrap.UploadCropbox = function(config){
29533     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29534     
29535     this.addEvents({
29536         /**
29537          * @event beforeselectfile
29538          * Fire before select file
29539          * @param {Roo.bootstrap.UploadCropbox} this
29540          */
29541         "beforeselectfile" : true,
29542         /**
29543          * @event initial
29544          * Fire after initEvent
29545          * @param {Roo.bootstrap.UploadCropbox} this
29546          */
29547         "initial" : true,
29548         /**
29549          * @event crop
29550          * Fire after initEvent
29551          * @param {Roo.bootstrap.UploadCropbox} this
29552          * @param {String} data
29553          */
29554         "crop" : true,
29555         /**
29556          * @event prepare
29557          * Fire when preparing the file data
29558          * @param {Roo.bootstrap.UploadCropbox} this
29559          * @param {Object} file
29560          */
29561         "prepare" : true,
29562         /**
29563          * @event exception
29564          * Fire when get exception
29565          * @param {Roo.bootstrap.UploadCropbox} this
29566          * @param {XMLHttpRequest} xhr
29567          */
29568         "exception" : true,
29569         /**
29570          * @event beforeloadcanvas
29571          * Fire before load the canvas
29572          * @param {Roo.bootstrap.UploadCropbox} this
29573          * @param {String} src
29574          */
29575         "beforeloadcanvas" : true,
29576         /**
29577          * @event trash
29578          * Fire when trash image
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          */
29581         "trash" : true,
29582         /**
29583          * @event download
29584          * Fire when download the image
29585          * @param {Roo.bootstrap.UploadCropbox} this
29586          */
29587         "download" : true,
29588         /**
29589          * @event footerbuttonclick
29590          * Fire when footerbuttonclick
29591          * @param {Roo.bootstrap.UploadCropbox} this
29592          * @param {String} type
29593          */
29594         "footerbuttonclick" : true,
29595         /**
29596          * @event resize
29597          * Fire when resize
29598          * @param {Roo.bootstrap.UploadCropbox} this
29599          */
29600         "resize" : true,
29601         /**
29602          * @event rotate
29603          * Fire when rotate the image
29604          * @param {Roo.bootstrap.UploadCropbox} this
29605          * @param {String} pos
29606          */
29607         "rotate" : true,
29608         /**
29609          * @event inspect
29610          * Fire when inspect the file
29611          * @param {Roo.bootstrap.UploadCropbox} this
29612          * @param {Object} file
29613          */
29614         "inspect" : true,
29615         /**
29616          * @event upload
29617          * Fire when xhr upload the file
29618          * @param {Roo.bootstrap.UploadCropbox} this
29619          * @param {Object} data
29620          */
29621         "upload" : true,
29622         /**
29623          * @event arrange
29624          * Fire when arrange the file data
29625          * @param {Roo.bootstrap.UploadCropbox} this
29626          * @param {Object} formData
29627          */
29628         "arrange" : true
29629     });
29630     
29631     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29632 };
29633
29634 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29635     
29636     emptyText : 'Click to upload image',
29637     rotateNotify : 'Image is too small to rotate',
29638     errorTimeout : 3000,
29639     scale : 0,
29640     baseScale : 1,
29641     rotate : 0,
29642     dragable : false,
29643     pinching : false,
29644     mouseX : 0,
29645     mouseY : 0,
29646     cropData : false,
29647     minWidth : 300,
29648     minHeight : 300,
29649     file : false,
29650     exif : {},
29651     baseRotate : 1,
29652     cropType : 'image/jpeg',
29653     buttons : false,
29654     canvasLoaded : false,
29655     isDocument : false,
29656     method : 'POST',
29657     paramName : 'imageUpload',
29658     loadMask : true,
29659     loadingText : 'Loading...',
29660     maskEl : false,
29661     
29662     getAutoCreate : function()
29663     {
29664         var cfg = {
29665             tag : 'div',
29666             cls : 'roo-upload-cropbox',
29667             cn : [
29668                 {
29669                     tag : 'input',
29670                     cls : 'roo-upload-cropbox-selector',
29671                     type : 'file'
29672                 },
29673                 {
29674                     tag : 'div',
29675                     cls : 'roo-upload-cropbox-body',
29676                     style : 'cursor:pointer',
29677                     cn : [
29678                         {
29679                             tag : 'div',
29680                             cls : 'roo-upload-cropbox-preview'
29681                         },
29682                         {
29683                             tag : 'div',
29684                             cls : 'roo-upload-cropbox-thumb'
29685                         },
29686                         {
29687                             tag : 'div',
29688                             cls : 'roo-upload-cropbox-empty-notify',
29689                             html : this.emptyText
29690                         },
29691                         {
29692                             tag : 'div',
29693                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29694                             html : this.rotateNotify
29695                         }
29696                     ]
29697                 },
29698                 {
29699                     tag : 'div',
29700                     cls : 'roo-upload-cropbox-footer',
29701                     cn : {
29702                         tag : 'div',
29703                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29704                         cn : []
29705                     }
29706                 }
29707             ]
29708         };
29709         
29710         return cfg;
29711     },
29712     
29713     onRender : function(ct, position)
29714     {
29715         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29716         
29717         if (this.buttons.length) {
29718             
29719             Roo.each(this.buttons, function(bb) {
29720                 
29721                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29722                 
29723                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29724                 
29725             }, this);
29726         }
29727         
29728         if(this.loadMask){
29729             this.maskEl = this.el;
29730         }
29731     },
29732     
29733     initEvents : function()
29734     {
29735         this.urlAPI = (window.createObjectURL && window) || 
29736                                 (window.URL && URL.revokeObjectURL && URL) || 
29737                                 (window.webkitURL && webkitURL);
29738                         
29739         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29740         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29741         
29742         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29743         this.selectorEl.hide();
29744         
29745         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29746         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29747         
29748         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29749         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29750         this.thumbEl.hide();
29751         
29752         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29753         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29754         
29755         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29756         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29757         this.errorEl.hide();
29758         
29759         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29760         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29761         this.footerEl.hide();
29762         
29763         this.setThumbBoxSize();
29764         
29765         this.bind();
29766         
29767         this.resize();
29768         
29769         this.fireEvent('initial', this);
29770     },
29771
29772     bind : function()
29773     {
29774         var _this = this;
29775         
29776         window.addEventListener("resize", function() { _this.resize(); } );
29777         
29778         this.bodyEl.on('click', this.beforeSelectFile, this);
29779         
29780         if(Roo.isTouch){
29781             this.bodyEl.on('touchstart', this.onTouchStart, this);
29782             this.bodyEl.on('touchmove', this.onTouchMove, this);
29783             this.bodyEl.on('touchend', this.onTouchEnd, this);
29784         }
29785         
29786         if(!Roo.isTouch){
29787             this.bodyEl.on('mousedown', this.onMouseDown, this);
29788             this.bodyEl.on('mousemove', this.onMouseMove, this);
29789             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29790             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29791             Roo.get(document).on('mouseup', this.onMouseUp, this);
29792         }
29793         
29794         this.selectorEl.on('change', this.onFileSelected, this);
29795     },
29796     
29797     reset : function()
29798     {    
29799         this.scale = 0;
29800         this.baseScale = 1;
29801         this.rotate = 0;
29802         this.baseRotate = 1;
29803         this.dragable = false;
29804         this.pinching = false;
29805         this.mouseX = 0;
29806         this.mouseY = 0;
29807         this.cropData = false;
29808         this.notifyEl.dom.innerHTML = this.emptyText;
29809         
29810         this.selectorEl.dom.value = '';
29811         
29812     },
29813     
29814     resize : function()
29815     {
29816         if(this.fireEvent('resize', this) != false){
29817             this.setThumbBoxPosition();
29818             this.setCanvasPosition();
29819         }
29820     },
29821     
29822     onFooterButtonClick : function(e, el, o, type)
29823     {
29824         switch (type) {
29825             case 'rotate-left' :
29826                 this.onRotateLeft(e);
29827                 break;
29828             case 'rotate-right' :
29829                 this.onRotateRight(e);
29830                 break;
29831             case 'picture' :
29832                 this.beforeSelectFile(e);
29833                 break;
29834             case 'trash' :
29835                 this.trash(e);
29836                 break;
29837             case 'crop' :
29838                 this.crop(e);
29839                 break;
29840             case 'download' :
29841                 this.download(e);
29842                 break;
29843             default :
29844                 break;
29845         }
29846         
29847         this.fireEvent('footerbuttonclick', this, type);
29848     },
29849     
29850     beforeSelectFile : function(e)
29851     {
29852         e.preventDefault();
29853         
29854         if(this.fireEvent('beforeselectfile', this) != false){
29855             this.selectorEl.dom.click();
29856         }
29857     },
29858     
29859     onFileSelected : function(e)
29860     {
29861         e.preventDefault();
29862         
29863         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29864             return;
29865         }
29866         
29867         var file = this.selectorEl.dom.files[0];
29868         
29869         if(this.fireEvent('inspect', this, file) != false){
29870             this.prepare(file);
29871         }
29872         
29873     },
29874     
29875     trash : function(e)
29876     {
29877         this.fireEvent('trash', this);
29878     },
29879     
29880     download : function(e)
29881     {
29882         this.fireEvent('download', this);
29883     },
29884     
29885     loadCanvas : function(src)
29886     {   
29887         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29888             
29889             this.reset();
29890             
29891             this.imageEl = document.createElement('img');
29892             
29893             var _this = this;
29894             
29895             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29896             
29897             this.imageEl.src = src;
29898         }
29899     },
29900     
29901     onLoadCanvas : function()
29902     {   
29903         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29904         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29905         
29906         this.bodyEl.un('click', this.beforeSelectFile, this);
29907         
29908         this.notifyEl.hide();
29909         this.thumbEl.show();
29910         this.footerEl.show();
29911         
29912         this.baseRotateLevel();
29913         
29914         if(this.isDocument){
29915             this.setThumbBoxSize();
29916         }
29917         
29918         this.setThumbBoxPosition();
29919         
29920         this.baseScaleLevel();
29921         
29922         this.draw();
29923         
29924         this.resize();
29925         
29926         this.canvasLoaded = true;
29927         
29928         if(this.loadMask){
29929             this.maskEl.unmask();
29930         }
29931         
29932     },
29933     
29934     setCanvasPosition : function()
29935     {   
29936         if(!this.canvasEl){
29937             return;
29938         }
29939         
29940         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29941         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29942         
29943         this.previewEl.setLeft(pw);
29944         this.previewEl.setTop(ph);
29945         
29946     },
29947     
29948     onMouseDown : function(e)
29949     {   
29950         e.stopEvent();
29951         
29952         this.dragable = true;
29953         this.pinching = false;
29954         
29955         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29956             this.dragable = false;
29957             return;
29958         }
29959         
29960         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29961         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29962         
29963     },
29964     
29965     onMouseMove : function(e)
29966     {   
29967         e.stopEvent();
29968         
29969         if(!this.canvasLoaded){
29970             return;
29971         }
29972         
29973         if (!this.dragable){
29974             return;
29975         }
29976         
29977         var minX = Math.ceil(this.thumbEl.getLeft(true));
29978         var minY = Math.ceil(this.thumbEl.getTop(true));
29979         
29980         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29981         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29982         
29983         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29984         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29985         
29986         x = x - this.mouseX;
29987         y = y - this.mouseY;
29988         
29989         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29990         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29991         
29992         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29993         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29994         
29995         this.previewEl.setLeft(bgX);
29996         this.previewEl.setTop(bgY);
29997         
29998         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29999         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30000     },
30001     
30002     onMouseUp : function(e)
30003     {   
30004         e.stopEvent();
30005         
30006         this.dragable = false;
30007     },
30008     
30009     onMouseWheel : function(e)
30010     {   
30011         e.stopEvent();
30012         
30013         this.startScale = this.scale;
30014         
30015         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30016         
30017         if(!this.zoomable()){
30018             this.scale = this.startScale;
30019             return;
30020         }
30021         
30022         this.draw();
30023         
30024         return;
30025     },
30026     
30027     zoomable : function()
30028     {
30029         var minScale = this.thumbEl.getWidth() / this.minWidth;
30030         
30031         if(this.minWidth < this.minHeight){
30032             minScale = this.thumbEl.getHeight() / this.minHeight;
30033         }
30034         
30035         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30036         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30037         
30038         if(
30039                 this.isDocument &&
30040                 (this.rotate == 0 || this.rotate == 180) && 
30041                 (
30042                     width > this.imageEl.OriginWidth || 
30043                     height > this.imageEl.OriginHeight ||
30044                     (width < this.minWidth && height < this.minHeight)
30045                 )
30046         ){
30047             return false;
30048         }
30049         
30050         if(
30051                 this.isDocument &&
30052                 (this.rotate == 90 || this.rotate == 270) && 
30053                 (
30054                     width > this.imageEl.OriginWidth || 
30055                     height > this.imageEl.OriginHeight ||
30056                     (width < this.minHeight && height < this.minWidth)
30057                 )
30058         ){
30059             return false;
30060         }
30061         
30062         if(
30063                 !this.isDocument &&
30064                 (this.rotate == 0 || this.rotate == 180) && 
30065                 (
30066                     width < this.minWidth || 
30067                     width > this.imageEl.OriginWidth || 
30068                     height < this.minHeight || 
30069                     height > this.imageEl.OriginHeight
30070                 )
30071         ){
30072             return false;
30073         }
30074         
30075         if(
30076                 !this.isDocument &&
30077                 (this.rotate == 90 || this.rotate == 270) && 
30078                 (
30079                     width < this.minHeight || 
30080                     width > this.imageEl.OriginWidth || 
30081                     height < this.minWidth || 
30082                     height > this.imageEl.OriginHeight
30083                 )
30084         ){
30085             return false;
30086         }
30087         
30088         return true;
30089         
30090     },
30091     
30092     onRotateLeft : function(e)
30093     {   
30094         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30095             
30096             var minScale = this.thumbEl.getWidth() / this.minWidth;
30097             
30098             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30099             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30100             
30101             this.startScale = this.scale;
30102             
30103             while (this.getScaleLevel() < minScale){
30104             
30105                 this.scale = this.scale + 1;
30106                 
30107                 if(!this.zoomable()){
30108                     break;
30109                 }
30110                 
30111                 if(
30112                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30113                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30114                 ){
30115                     continue;
30116                 }
30117                 
30118                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30119
30120                 this.draw();
30121                 
30122                 return;
30123             }
30124             
30125             this.scale = this.startScale;
30126             
30127             this.onRotateFail();
30128             
30129             return false;
30130         }
30131         
30132         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30133
30134         if(this.isDocument){
30135             this.setThumbBoxSize();
30136             this.setThumbBoxPosition();
30137             this.setCanvasPosition();
30138         }
30139         
30140         this.draw();
30141         
30142         this.fireEvent('rotate', this, 'left');
30143         
30144     },
30145     
30146     onRotateRight : function(e)
30147     {
30148         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30149             
30150             var minScale = this.thumbEl.getWidth() / this.minWidth;
30151         
30152             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30153             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30154             
30155             this.startScale = this.scale;
30156             
30157             while (this.getScaleLevel() < minScale){
30158             
30159                 this.scale = this.scale + 1;
30160                 
30161                 if(!this.zoomable()){
30162                     break;
30163                 }
30164                 
30165                 if(
30166                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30167                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30168                 ){
30169                     continue;
30170                 }
30171                 
30172                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30173
30174                 this.draw();
30175                 
30176                 return;
30177             }
30178             
30179             this.scale = this.startScale;
30180             
30181             this.onRotateFail();
30182             
30183             return false;
30184         }
30185         
30186         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30187
30188         if(this.isDocument){
30189             this.setThumbBoxSize();
30190             this.setThumbBoxPosition();
30191             this.setCanvasPosition();
30192         }
30193         
30194         this.draw();
30195         
30196         this.fireEvent('rotate', this, 'right');
30197     },
30198     
30199     onRotateFail : function()
30200     {
30201         this.errorEl.show(true);
30202         
30203         var _this = this;
30204         
30205         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30206     },
30207     
30208     draw : function()
30209     {
30210         this.previewEl.dom.innerHTML = '';
30211         
30212         var canvasEl = document.createElement("canvas");
30213         
30214         var contextEl = canvasEl.getContext("2d");
30215         
30216         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30217         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30218         var center = this.imageEl.OriginWidth / 2;
30219         
30220         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30221             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30222             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30223             center = this.imageEl.OriginHeight / 2;
30224         }
30225         
30226         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30227         
30228         contextEl.translate(center, center);
30229         contextEl.rotate(this.rotate * Math.PI / 180);
30230
30231         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30232         
30233         this.canvasEl = document.createElement("canvas");
30234         
30235         this.contextEl = this.canvasEl.getContext("2d");
30236         
30237         switch (this.rotate) {
30238             case 0 :
30239                 
30240                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30241                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30242                 
30243                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30244                 
30245                 break;
30246             case 90 : 
30247                 
30248                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30249                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30250                 
30251                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30252                     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);
30253                     break;
30254                 }
30255                 
30256                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30257                 
30258                 break;
30259             case 180 :
30260                 
30261                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30262                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30263                 
30264                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30265                     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);
30266                     break;
30267                 }
30268                 
30269                 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);
30270                 
30271                 break;
30272             case 270 :
30273                 
30274                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30275                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30276         
30277                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30278                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30279                     break;
30280                 }
30281                 
30282                 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);
30283                 
30284                 break;
30285             default : 
30286                 break;
30287         }
30288         
30289         this.previewEl.appendChild(this.canvasEl);
30290         
30291         this.setCanvasPosition();
30292     },
30293     
30294     crop : function()
30295     {
30296         if(!this.canvasLoaded){
30297             return;
30298         }
30299         
30300         var imageCanvas = document.createElement("canvas");
30301         
30302         var imageContext = imageCanvas.getContext("2d");
30303         
30304         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30305         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30306         
30307         var center = imageCanvas.width / 2;
30308         
30309         imageContext.translate(center, center);
30310         
30311         imageContext.rotate(this.rotate * Math.PI / 180);
30312         
30313         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30314         
30315         var canvas = document.createElement("canvas");
30316         
30317         var context = canvas.getContext("2d");
30318                 
30319         canvas.width = this.minWidth;
30320         canvas.height = this.minHeight;
30321
30322         switch (this.rotate) {
30323             case 0 :
30324                 
30325                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30326                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30327                 
30328                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30329                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30330                 
30331                 var targetWidth = this.minWidth - 2 * x;
30332                 var targetHeight = this.minHeight - 2 * y;
30333                 
30334                 var scale = 1;
30335                 
30336                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30337                     scale = targetWidth / width;
30338                 }
30339                 
30340                 if(x > 0 && y == 0){
30341                     scale = targetHeight / height;
30342                 }
30343                 
30344                 if(x > 0 && y > 0){
30345                     scale = targetWidth / width;
30346                     
30347                     if(width < height){
30348                         scale = targetHeight / height;
30349                     }
30350                 }
30351                 
30352                 context.scale(scale, scale);
30353                 
30354                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30355                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30356
30357                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30358                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30359
30360                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30361                 
30362                 break;
30363             case 90 : 
30364                 
30365                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30366                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30367                 
30368                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30369                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30370                 
30371                 var targetWidth = this.minWidth - 2 * x;
30372                 var targetHeight = this.minHeight - 2 * y;
30373                 
30374                 var scale = 1;
30375                 
30376                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30377                     scale = targetWidth / width;
30378                 }
30379                 
30380                 if(x > 0 && y == 0){
30381                     scale = targetHeight / height;
30382                 }
30383                 
30384                 if(x > 0 && y > 0){
30385                     scale = targetWidth / width;
30386                     
30387                     if(width < height){
30388                         scale = targetHeight / height;
30389                     }
30390                 }
30391                 
30392                 context.scale(scale, scale);
30393                 
30394                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30395                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30396
30397                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30398                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30399                 
30400                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30401                 
30402                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30403                 
30404                 break;
30405             case 180 :
30406                 
30407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30409                 
30410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30412                 
30413                 var targetWidth = this.minWidth - 2 * x;
30414                 var targetHeight = this.minHeight - 2 * y;
30415                 
30416                 var scale = 1;
30417                 
30418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30419                     scale = targetWidth / width;
30420                 }
30421                 
30422                 if(x > 0 && y == 0){
30423                     scale = targetHeight / height;
30424                 }
30425                 
30426                 if(x > 0 && y > 0){
30427                     scale = targetWidth / width;
30428                     
30429                     if(width < height){
30430                         scale = targetHeight / height;
30431                     }
30432                 }
30433                 
30434                 context.scale(scale, scale);
30435                 
30436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30438
30439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30441
30442                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30443                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30444                 
30445                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30446                 
30447                 break;
30448             case 270 :
30449                 
30450                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30451                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30452                 
30453                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30454                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30455                 
30456                 var targetWidth = this.minWidth - 2 * x;
30457                 var targetHeight = this.minHeight - 2 * y;
30458                 
30459                 var scale = 1;
30460                 
30461                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30462                     scale = targetWidth / width;
30463                 }
30464                 
30465                 if(x > 0 && y == 0){
30466                     scale = targetHeight / height;
30467                 }
30468                 
30469                 if(x > 0 && y > 0){
30470                     scale = targetWidth / width;
30471                     
30472                     if(width < height){
30473                         scale = targetHeight / height;
30474                     }
30475                 }
30476                 
30477                 context.scale(scale, scale);
30478                 
30479                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30480                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30481
30482                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30483                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30484                 
30485                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30486                 
30487                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30488                 
30489                 break;
30490             default : 
30491                 break;
30492         }
30493         
30494         this.cropData = canvas.toDataURL(this.cropType);
30495         
30496         if(this.fireEvent('crop', this, this.cropData) !== false){
30497             this.process(this.file, this.cropData);
30498         }
30499         
30500         return;
30501         
30502     },
30503     
30504     setThumbBoxSize : function()
30505     {
30506         var width, height;
30507         
30508         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30509             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30510             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30511             
30512             this.minWidth = width;
30513             this.minHeight = height;
30514             
30515             if(this.rotate == 90 || this.rotate == 270){
30516                 this.minWidth = height;
30517                 this.minHeight = width;
30518             }
30519         }
30520         
30521         height = 300;
30522         width = Math.ceil(this.minWidth * height / this.minHeight);
30523         
30524         if(this.minWidth > this.minHeight){
30525             width = 300;
30526             height = Math.ceil(this.minHeight * width / this.minWidth);
30527         }
30528         
30529         this.thumbEl.setStyle({
30530             width : width + 'px',
30531             height : height + 'px'
30532         });
30533
30534         return;
30535             
30536     },
30537     
30538     setThumbBoxPosition : function()
30539     {
30540         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30541         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30542         
30543         this.thumbEl.setLeft(x);
30544         this.thumbEl.setTop(y);
30545         
30546     },
30547     
30548     baseRotateLevel : function()
30549     {
30550         this.baseRotate = 1;
30551         
30552         if(
30553                 typeof(this.exif) != 'undefined' &&
30554                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30555                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30556         ){
30557             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30558         }
30559         
30560         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30561         
30562     },
30563     
30564     baseScaleLevel : function()
30565     {
30566         var width, height;
30567         
30568         if(this.isDocument){
30569             
30570             if(this.baseRotate == 6 || this.baseRotate == 8){
30571             
30572                 height = this.thumbEl.getHeight();
30573                 this.baseScale = height / this.imageEl.OriginWidth;
30574
30575                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30576                     width = this.thumbEl.getWidth();
30577                     this.baseScale = width / this.imageEl.OriginHeight;
30578                 }
30579
30580                 return;
30581             }
30582
30583             height = this.thumbEl.getHeight();
30584             this.baseScale = height / this.imageEl.OriginHeight;
30585
30586             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30587                 width = this.thumbEl.getWidth();
30588                 this.baseScale = width / this.imageEl.OriginWidth;
30589             }
30590
30591             return;
30592         }
30593         
30594         if(this.baseRotate == 6 || this.baseRotate == 8){
30595             
30596             width = this.thumbEl.getHeight();
30597             this.baseScale = width / this.imageEl.OriginHeight;
30598             
30599             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30600                 height = this.thumbEl.getWidth();
30601                 this.baseScale = height / this.imageEl.OriginHeight;
30602             }
30603             
30604             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30605                 height = this.thumbEl.getWidth();
30606                 this.baseScale = height / this.imageEl.OriginHeight;
30607                 
30608                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30609                     width = this.thumbEl.getHeight();
30610                     this.baseScale = width / this.imageEl.OriginWidth;
30611                 }
30612             }
30613             
30614             return;
30615         }
30616         
30617         width = this.thumbEl.getWidth();
30618         this.baseScale = width / this.imageEl.OriginWidth;
30619         
30620         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30621             height = this.thumbEl.getHeight();
30622             this.baseScale = height / this.imageEl.OriginHeight;
30623         }
30624         
30625         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30626             
30627             height = this.thumbEl.getHeight();
30628             this.baseScale = height / this.imageEl.OriginHeight;
30629             
30630             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30631                 width = this.thumbEl.getWidth();
30632                 this.baseScale = width / this.imageEl.OriginWidth;
30633             }
30634             
30635         }
30636         
30637         return;
30638     },
30639     
30640     getScaleLevel : function()
30641     {
30642         return this.baseScale * Math.pow(1.1, this.scale);
30643     },
30644     
30645     onTouchStart : function(e)
30646     {
30647         if(!this.canvasLoaded){
30648             this.beforeSelectFile(e);
30649             return;
30650         }
30651         
30652         var touches = e.browserEvent.touches;
30653         
30654         if(!touches){
30655             return;
30656         }
30657         
30658         if(touches.length == 1){
30659             this.onMouseDown(e);
30660             return;
30661         }
30662         
30663         if(touches.length != 2){
30664             return;
30665         }
30666         
30667         var coords = [];
30668         
30669         for(var i = 0, finger; finger = touches[i]; i++){
30670             coords.push(finger.pageX, finger.pageY);
30671         }
30672         
30673         var x = Math.pow(coords[0] - coords[2], 2);
30674         var y = Math.pow(coords[1] - coords[3], 2);
30675         
30676         this.startDistance = Math.sqrt(x + y);
30677         
30678         this.startScale = this.scale;
30679         
30680         this.pinching = true;
30681         this.dragable = false;
30682         
30683     },
30684     
30685     onTouchMove : function(e)
30686     {
30687         if(!this.pinching && !this.dragable){
30688             return;
30689         }
30690         
30691         var touches = e.browserEvent.touches;
30692         
30693         if(!touches){
30694             return;
30695         }
30696         
30697         if(this.dragable){
30698             this.onMouseMove(e);
30699             return;
30700         }
30701         
30702         var coords = [];
30703         
30704         for(var i = 0, finger; finger = touches[i]; i++){
30705             coords.push(finger.pageX, finger.pageY);
30706         }
30707         
30708         var x = Math.pow(coords[0] - coords[2], 2);
30709         var y = Math.pow(coords[1] - coords[3], 2);
30710         
30711         this.endDistance = Math.sqrt(x + y);
30712         
30713         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30714         
30715         if(!this.zoomable()){
30716             this.scale = this.startScale;
30717             return;
30718         }
30719         
30720         this.draw();
30721         
30722     },
30723     
30724     onTouchEnd : function(e)
30725     {
30726         this.pinching = false;
30727         this.dragable = false;
30728         
30729     },
30730     
30731     process : function(file, crop)
30732     {
30733         if(this.loadMask){
30734             this.maskEl.mask(this.loadingText);
30735         }
30736         
30737         this.xhr = new XMLHttpRequest();
30738         
30739         file.xhr = this.xhr;
30740
30741         this.xhr.open(this.method, this.url, true);
30742         
30743         var headers = {
30744             "Accept": "application/json",
30745             "Cache-Control": "no-cache",
30746             "X-Requested-With": "XMLHttpRequest"
30747         };
30748         
30749         for (var headerName in headers) {
30750             var headerValue = headers[headerName];
30751             if (headerValue) {
30752                 this.xhr.setRequestHeader(headerName, headerValue);
30753             }
30754         }
30755         
30756         var _this = this;
30757         
30758         this.xhr.onload = function()
30759         {
30760             _this.xhrOnLoad(_this.xhr);
30761         }
30762         
30763         this.xhr.onerror = function()
30764         {
30765             _this.xhrOnError(_this.xhr);
30766         }
30767         
30768         var formData = new FormData();
30769
30770         formData.append('returnHTML', 'NO');
30771         
30772         if(crop){
30773             formData.append('crop', crop);
30774         }
30775         
30776         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30777             formData.append(this.paramName, file, file.name);
30778         }
30779         
30780         if(typeof(file.filename) != 'undefined'){
30781             formData.append('filename', file.filename);
30782         }
30783         
30784         if(typeof(file.mimetype) != 'undefined'){
30785             formData.append('mimetype', file.mimetype);
30786         }
30787         
30788         if(this.fireEvent('arrange', this, formData) != false){
30789             this.xhr.send(formData);
30790         };
30791     },
30792     
30793     xhrOnLoad : function(xhr)
30794     {
30795         if(this.loadMask){
30796             this.maskEl.unmask();
30797         }
30798         
30799         if (xhr.readyState !== 4) {
30800             this.fireEvent('exception', this, xhr);
30801             return;
30802         }
30803
30804         var response = Roo.decode(xhr.responseText);
30805         
30806         if(!response.success){
30807             this.fireEvent('exception', this, xhr);
30808             return;
30809         }
30810         
30811         var response = Roo.decode(xhr.responseText);
30812         
30813         this.fireEvent('upload', this, response);
30814         
30815     },
30816     
30817     xhrOnError : function()
30818     {
30819         if(this.loadMask){
30820             this.maskEl.unmask();
30821         }
30822         
30823         Roo.log('xhr on error');
30824         
30825         var response = Roo.decode(xhr.responseText);
30826           
30827         Roo.log(response);
30828         
30829     },
30830     
30831     prepare : function(file)
30832     {   
30833         if(this.loadMask){
30834             this.maskEl.mask(this.loadingText);
30835         }
30836         
30837         this.file = false;
30838         this.exif = {};
30839         
30840         if(typeof(file) === 'string'){
30841             this.loadCanvas(file);
30842             return;
30843         }
30844         
30845         if(!file || !this.urlAPI){
30846             return;
30847         }
30848         
30849         this.file = file;
30850         this.cropType = file.type;
30851         
30852         var _this = this;
30853         
30854         if(this.fireEvent('prepare', this, this.file) != false){
30855             
30856             var reader = new FileReader();
30857             
30858             reader.onload = function (e) {
30859                 if (e.target.error) {
30860                     Roo.log(e.target.error);
30861                     return;
30862                 }
30863                 
30864                 var buffer = e.target.result,
30865                     dataView = new DataView(buffer),
30866                     offset = 2,
30867                     maxOffset = dataView.byteLength - 4,
30868                     markerBytes,
30869                     markerLength;
30870                 
30871                 if (dataView.getUint16(0) === 0xffd8) {
30872                     while (offset < maxOffset) {
30873                         markerBytes = dataView.getUint16(offset);
30874                         
30875                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30876                             markerLength = dataView.getUint16(offset + 2) + 2;
30877                             if (offset + markerLength > dataView.byteLength) {
30878                                 Roo.log('Invalid meta data: Invalid segment size.');
30879                                 break;
30880                             }
30881                             
30882                             if(markerBytes == 0xffe1){
30883                                 _this.parseExifData(
30884                                     dataView,
30885                                     offset,
30886                                     markerLength
30887                                 );
30888                             }
30889                             
30890                             offset += markerLength;
30891                             
30892                             continue;
30893                         }
30894                         
30895                         break;
30896                     }
30897                     
30898                 }
30899                 
30900                 var url = _this.urlAPI.createObjectURL(_this.file);
30901                 
30902                 _this.loadCanvas(url);
30903                 
30904                 return;
30905             }
30906             
30907             reader.readAsArrayBuffer(this.file);
30908             
30909         }
30910         
30911     },
30912     
30913     parseExifData : function(dataView, offset, length)
30914     {
30915         var tiffOffset = offset + 10,
30916             littleEndian,
30917             dirOffset;
30918     
30919         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30920             // No Exif data, might be XMP data instead
30921             return;
30922         }
30923         
30924         // Check for the ASCII code for "Exif" (0x45786966):
30925         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30926             // No Exif data, might be XMP data instead
30927             return;
30928         }
30929         if (tiffOffset + 8 > dataView.byteLength) {
30930             Roo.log('Invalid Exif data: Invalid segment size.');
30931             return;
30932         }
30933         // Check for the two null bytes:
30934         if (dataView.getUint16(offset + 8) !== 0x0000) {
30935             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30936             return;
30937         }
30938         // Check the byte alignment:
30939         switch (dataView.getUint16(tiffOffset)) {
30940         case 0x4949:
30941             littleEndian = true;
30942             break;
30943         case 0x4D4D:
30944             littleEndian = false;
30945             break;
30946         default:
30947             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30948             return;
30949         }
30950         // Check for the TIFF tag marker (0x002A):
30951         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30952             Roo.log('Invalid Exif data: Missing TIFF marker.');
30953             return;
30954         }
30955         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30956         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30957         
30958         this.parseExifTags(
30959             dataView,
30960             tiffOffset,
30961             tiffOffset + dirOffset,
30962             littleEndian
30963         );
30964     },
30965     
30966     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30967     {
30968         var tagsNumber,
30969             dirEndOffset,
30970             i;
30971         if (dirOffset + 6 > dataView.byteLength) {
30972             Roo.log('Invalid Exif data: Invalid directory offset.');
30973             return;
30974         }
30975         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30976         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30977         if (dirEndOffset + 4 > dataView.byteLength) {
30978             Roo.log('Invalid Exif data: Invalid directory size.');
30979             return;
30980         }
30981         for (i = 0; i < tagsNumber; i += 1) {
30982             this.parseExifTag(
30983                 dataView,
30984                 tiffOffset,
30985                 dirOffset + 2 + 12 * i, // tag offset
30986                 littleEndian
30987             );
30988         }
30989         // Return the offset to the next directory:
30990         return dataView.getUint32(dirEndOffset, littleEndian);
30991     },
30992     
30993     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30994     {
30995         var tag = dataView.getUint16(offset, littleEndian);
30996         
30997         this.exif[tag] = this.getExifValue(
30998             dataView,
30999             tiffOffset,
31000             offset,
31001             dataView.getUint16(offset + 2, littleEndian), // tag type
31002             dataView.getUint32(offset + 4, littleEndian), // tag length
31003             littleEndian
31004         );
31005     },
31006     
31007     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31008     {
31009         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31010             tagSize,
31011             dataOffset,
31012             values,
31013             i,
31014             str,
31015             c;
31016     
31017         if (!tagType) {
31018             Roo.log('Invalid Exif data: Invalid tag type.');
31019             return;
31020         }
31021         
31022         tagSize = tagType.size * length;
31023         // Determine if the value is contained in the dataOffset bytes,
31024         // or if the value at the dataOffset is a pointer to the actual data:
31025         dataOffset = tagSize > 4 ?
31026                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31027         if (dataOffset + tagSize > dataView.byteLength) {
31028             Roo.log('Invalid Exif data: Invalid data offset.');
31029             return;
31030         }
31031         if (length === 1) {
31032             return tagType.getValue(dataView, dataOffset, littleEndian);
31033         }
31034         values = [];
31035         for (i = 0; i < length; i += 1) {
31036             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31037         }
31038         
31039         if (tagType.ascii) {
31040             str = '';
31041             // Concatenate the chars:
31042             for (i = 0; i < values.length; i += 1) {
31043                 c = values[i];
31044                 // Ignore the terminating NULL byte(s):
31045                 if (c === '\u0000') {
31046                     break;
31047                 }
31048                 str += c;
31049             }
31050             return str;
31051         }
31052         return values;
31053     }
31054     
31055 });
31056
31057 Roo.apply(Roo.bootstrap.UploadCropbox, {
31058     tags : {
31059         'Orientation': 0x0112
31060     },
31061     
31062     Orientation: {
31063             1: 0, //'top-left',
31064 //            2: 'top-right',
31065             3: 180, //'bottom-right',
31066 //            4: 'bottom-left',
31067 //            5: 'left-top',
31068             6: 90, //'right-top',
31069 //            7: 'right-bottom',
31070             8: 270 //'left-bottom'
31071     },
31072     
31073     exifTagTypes : {
31074         // byte, 8-bit unsigned int:
31075         1: {
31076             getValue: function (dataView, dataOffset) {
31077                 return dataView.getUint8(dataOffset);
31078             },
31079             size: 1
31080         },
31081         // ascii, 8-bit byte:
31082         2: {
31083             getValue: function (dataView, dataOffset) {
31084                 return String.fromCharCode(dataView.getUint8(dataOffset));
31085             },
31086             size: 1,
31087             ascii: true
31088         },
31089         // short, 16 bit int:
31090         3: {
31091             getValue: function (dataView, dataOffset, littleEndian) {
31092                 return dataView.getUint16(dataOffset, littleEndian);
31093             },
31094             size: 2
31095         },
31096         // long, 32 bit int:
31097         4: {
31098             getValue: function (dataView, dataOffset, littleEndian) {
31099                 return dataView.getUint32(dataOffset, littleEndian);
31100             },
31101             size: 4
31102         },
31103         // rational = two long values, first is numerator, second is denominator:
31104         5: {
31105             getValue: function (dataView, dataOffset, littleEndian) {
31106                 return dataView.getUint32(dataOffset, littleEndian) /
31107                     dataView.getUint32(dataOffset + 4, littleEndian);
31108             },
31109             size: 8
31110         },
31111         // slong, 32 bit signed int:
31112         9: {
31113             getValue: function (dataView, dataOffset, littleEndian) {
31114                 return dataView.getInt32(dataOffset, littleEndian);
31115             },
31116             size: 4
31117         },
31118         // srational, two slongs, first is numerator, second is denominator:
31119         10: {
31120             getValue: function (dataView, dataOffset, littleEndian) {
31121                 return dataView.getInt32(dataOffset, littleEndian) /
31122                     dataView.getInt32(dataOffset + 4, littleEndian);
31123             },
31124             size: 8
31125         }
31126     },
31127     
31128     footer : {
31129         STANDARD : [
31130             {
31131                 tag : 'div',
31132                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31133                 action : 'rotate-left',
31134                 cn : [
31135                     {
31136                         tag : 'button',
31137                         cls : 'btn btn-default',
31138                         html : '<i class="fa fa-undo"></i>'
31139                     }
31140                 ]
31141             },
31142             {
31143                 tag : 'div',
31144                 cls : 'btn-group roo-upload-cropbox-picture',
31145                 action : 'picture',
31146                 cn : [
31147                     {
31148                         tag : 'button',
31149                         cls : 'btn btn-default',
31150                         html : '<i class="fa fa-picture-o"></i>'
31151                     }
31152                 ]
31153             },
31154             {
31155                 tag : 'div',
31156                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31157                 action : 'rotate-right',
31158                 cn : [
31159                     {
31160                         tag : 'button',
31161                         cls : 'btn btn-default',
31162                         html : '<i class="fa fa-repeat"></i>'
31163                     }
31164                 ]
31165             }
31166         ],
31167         DOCUMENT : [
31168             {
31169                 tag : 'div',
31170                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31171                 action : 'rotate-left',
31172                 cn : [
31173                     {
31174                         tag : 'button',
31175                         cls : 'btn btn-default',
31176                         html : '<i class="fa fa-undo"></i>'
31177                     }
31178                 ]
31179             },
31180             {
31181                 tag : 'div',
31182                 cls : 'btn-group roo-upload-cropbox-download',
31183                 action : 'download',
31184                 cn : [
31185                     {
31186                         tag : 'button',
31187                         cls : 'btn btn-default',
31188                         html : '<i class="fa fa-download"></i>'
31189                     }
31190                 ]
31191             },
31192             {
31193                 tag : 'div',
31194                 cls : 'btn-group roo-upload-cropbox-crop',
31195                 action : 'crop',
31196                 cn : [
31197                     {
31198                         tag : 'button',
31199                         cls : 'btn btn-default',
31200                         html : '<i class="fa fa-crop"></i>'
31201                     }
31202                 ]
31203             },
31204             {
31205                 tag : 'div',
31206                 cls : 'btn-group roo-upload-cropbox-trash',
31207                 action : 'trash',
31208                 cn : [
31209                     {
31210                         tag : 'button',
31211                         cls : 'btn btn-default',
31212                         html : '<i class="fa fa-trash"></i>'
31213                     }
31214                 ]
31215             },
31216             {
31217                 tag : 'div',
31218                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31219                 action : 'rotate-right',
31220                 cn : [
31221                     {
31222                         tag : 'button',
31223                         cls : 'btn btn-default',
31224                         html : '<i class="fa fa-repeat"></i>'
31225                     }
31226                 ]
31227             }
31228         ],
31229         ROTATOR : [
31230             {
31231                 tag : 'div',
31232                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31233                 action : 'rotate-left',
31234                 cn : [
31235                     {
31236                         tag : 'button',
31237                         cls : 'btn btn-default',
31238                         html : '<i class="fa fa-undo"></i>'
31239                     }
31240                 ]
31241             },
31242             {
31243                 tag : 'div',
31244                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31245                 action : 'rotate-right',
31246                 cn : [
31247                     {
31248                         tag : 'button',
31249                         cls : 'btn btn-default',
31250                         html : '<i class="fa fa-repeat"></i>'
31251                     }
31252                 ]
31253             }
31254         ]
31255     }
31256 });
31257
31258 /*
31259 * Licence: LGPL
31260 */
31261
31262 /**
31263  * @class Roo.bootstrap.DocumentManager
31264  * @extends Roo.bootstrap.Component
31265  * Bootstrap DocumentManager class
31266  * @cfg {String} paramName default 'imageUpload'
31267  * @cfg {String} toolTipName default 'filename'
31268  * @cfg {String} method default POST
31269  * @cfg {String} url action url
31270  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31271  * @cfg {Boolean} multiple multiple upload default true
31272  * @cfg {Number} thumbSize default 300
31273  * @cfg {String} fieldLabel
31274  * @cfg {Number} labelWidth default 4
31275  * @cfg {String} labelAlign (left|top) default left
31276  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31277 * @cfg {Number} labellg set the width of label (1-12)
31278  * @cfg {Number} labelmd set the width of label (1-12)
31279  * @cfg {Number} labelsm set the width of label (1-12)
31280  * @cfg {Number} labelxs set the width of label (1-12)
31281  * 
31282  * @constructor
31283  * Create a new DocumentManager
31284  * @param {Object} config The config object
31285  */
31286
31287 Roo.bootstrap.DocumentManager = function(config){
31288     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31289     
31290     this.files = [];
31291     this.delegates = [];
31292     
31293     this.addEvents({
31294         /**
31295          * @event initial
31296          * Fire when initial the DocumentManager
31297          * @param {Roo.bootstrap.DocumentManager} this
31298          */
31299         "initial" : true,
31300         /**
31301          * @event inspect
31302          * inspect selected file
31303          * @param {Roo.bootstrap.DocumentManager} this
31304          * @param {File} file
31305          */
31306         "inspect" : true,
31307         /**
31308          * @event exception
31309          * Fire when xhr load exception
31310          * @param {Roo.bootstrap.DocumentManager} this
31311          * @param {XMLHttpRequest} xhr
31312          */
31313         "exception" : true,
31314         /**
31315          * @event afterupload
31316          * Fire when xhr load exception
31317          * @param {Roo.bootstrap.DocumentManager} this
31318          * @param {XMLHttpRequest} xhr
31319          */
31320         "afterupload" : true,
31321         /**
31322          * @event prepare
31323          * prepare the form data
31324          * @param {Roo.bootstrap.DocumentManager} this
31325          * @param {Object} formData
31326          */
31327         "prepare" : true,
31328         /**
31329          * @event remove
31330          * Fire when remove the file
31331          * @param {Roo.bootstrap.DocumentManager} this
31332          * @param {Object} file
31333          */
31334         "remove" : true,
31335         /**
31336          * @event refresh
31337          * Fire after refresh the file
31338          * @param {Roo.bootstrap.DocumentManager} this
31339          */
31340         "refresh" : true,
31341         /**
31342          * @event click
31343          * Fire after click the image
31344          * @param {Roo.bootstrap.DocumentManager} this
31345          * @param {Object} file
31346          */
31347         "click" : true,
31348         /**
31349          * @event edit
31350          * Fire when upload a image and editable set to true
31351          * @param {Roo.bootstrap.DocumentManager} this
31352          * @param {Object} file
31353          */
31354         "edit" : true,
31355         /**
31356          * @event beforeselectfile
31357          * Fire before select file
31358          * @param {Roo.bootstrap.DocumentManager} this
31359          */
31360         "beforeselectfile" : true,
31361         /**
31362          * @event process
31363          * Fire before process file
31364          * @param {Roo.bootstrap.DocumentManager} this
31365          * @param {Object} file
31366          */
31367         "process" : true,
31368         /**
31369          * @event previewrendered
31370          * Fire when preview rendered
31371          * @param {Roo.bootstrap.DocumentManager} this
31372          * @param {Object} file
31373          */
31374         "previewrendered" : true,
31375         /**
31376          */
31377         "previewResize" : true
31378         
31379     });
31380 };
31381
31382 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31383     
31384     boxes : 0,
31385     inputName : '',
31386     thumbSize : 300,
31387     multiple : true,
31388     files : false,
31389     method : 'POST',
31390     url : '',
31391     paramName : 'imageUpload',
31392     toolTipName : 'filename',
31393     fieldLabel : '',
31394     labelWidth : 4,
31395     labelAlign : 'left',
31396     editable : true,
31397     delegates : false,
31398     xhr : false, 
31399     
31400     labellg : 0,
31401     labelmd : 0,
31402     labelsm : 0,
31403     labelxs : 0,
31404     
31405     getAutoCreate : function()
31406     {   
31407         var managerWidget = {
31408             tag : 'div',
31409             cls : 'roo-document-manager',
31410             cn : [
31411                 {
31412                     tag : 'input',
31413                     cls : 'roo-document-manager-selector',
31414                     type : 'file'
31415                 },
31416                 {
31417                     tag : 'div',
31418                     cls : 'roo-document-manager-uploader',
31419                     cn : [
31420                         {
31421                             tag : 'div',
31422                             cls : 'roo-document-manager-upload-btn',
31423                             html : '<i class="fa fa-plus"></i>'
31424                         }
31425                     ]
31426                     
31427                 }
31428             ]
31429         };
31430         
31431         var content = [
31432             {
31433                 tag : 'div',
31434                 cls : 'column col-md-12',
31435                 cn : managerWidget
31436             }
31437         ];
31438         
31439         if(this.fieldLabel.length){
31440             
31441             content = [
31442                 {
31443                     tag : 'div',
31444                     cls : 'column col-md-12',
31445                     html : this.fieldLabel
31446                 },
31447                 {
31448                     tag : 'div',
31449                     cls : 'column col-md-12',
31450                     cn : managerWidget
31451                 }
31452             ];
31453
31454             if(this.labelAlign == 'left'){
31455                 content = [
31456                     {
31457                         tag : 'div',
31458                         cls : 'column',
31459                         html : this.fieldLabel
31460                     },
31461                     {
31462                         tag : 'div',
31463                         cls : 'column',
31464                         cn : managerWidget
31465                     }
31466                 ];
31467                 
31468                 if(this.labelWidth > 12){
31469                     content[0].style = "width: " + this.labelWidth + 'px';
31470                 }
31471
31472                 if(this.labelWidth < 13 && this.labelmd == 0){
31473                     this.labelmd = this.labelWidth;
31474                 }
31475
31476                 if(this.labellg > 0){
31477                     content[0].cls += ' col-lg-' + this.labellg;
31478                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31479                 }
31480
31481                 if(this.labelmd > 0){
31482                     content[0].cls += ' col-md-' + this.labelmd;
31483                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31484                 }
31485
31486                 if(this.labelsm > 0){
31487                     content[0].cls += ' col-sm-' + this.labelsm;
31488                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31489                 }
31490
31491                 if(this.labelxs > 0){
31492                     content[0].cls += ' col-xs-' + this.labelxs;
31493                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31494                 }
31495                 
31496             }
31497         }
31498         
31499         var cfg = {
31500             tag : 'div',
31501             cls : 'row clearfix',
31502             cn : content
31503         };
31504         
31505         return cfg;
31506         
31507     },
31508     
31509     initEvents : function()
31510     {
31511         this.managerEl = this.el.select('.roo-document-manager', true).first();
31512         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31513         
31514         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31515         this.selectorEl.hide();
31516         
31517         if(this.multiple){
31518             this.selectorEl.attr('multiple', 'multiple');
31519         }
31520         
31521         this.selectorEl.on('change', this.onFileSelected, this);
31522         
31523         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31524         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31525         
31526         this.uploader.on('click', this.onUploaderClick, this);
31527         
31528         this.renderProgressDialog();
31529         
31530         var _this = this;
31531         
31532         window.addEventListener("resize", function() { _this.refresh(); } );
31533         
31534         this.fireEvent('initial', this);
31535     },
31536     
31537     renderProgressDialog : function()
31538     {
31539         var _this = this;
31540         
31541         this.progressDialog = new Roo.bootstrap.Modal({
31542             cls : 'roo-document-manager-progress-dialog',
31543             allow_close : false,
31544             animate : false,
31545             title : '',
31546             buttons : [
31547                 {
31548                     name  :'cancel',
31549                     weight : 'danger',
31550                     html : 'Cancel'
31551                 }
31552             ], 
31553             listeners : { 
31554                 btnclick : function() {
31555                     _this.uploadCancel();
31556                     this.hide();
31557                 }
31558             }
31559         });
31560          
31561         this.progressDialog.render(Roo.get(document.body));
31562          
31563         this.progress = new Roo.bootstrap.Progress({
31564             cls : 'roo-document-manager-progress',
31565             active : true,
31566             striped : true
31567         });
31568         
31569         this.progress.render(this.progressDialog.getChildContainer());
31570         
31571         this.progressBar = new Roo.bootstrap.ProgressBar({
31572             cls : 'roo-document-manager-progress-bar',
31573             aria_valuenow : 0,
31574             aria_valuemin : 0,
31575             aria_valuemax : 12,
31576             panel : 'success'
31577         });
31578         
31579         this.progressBar.render(this.progress.getChildContainer());
31580     },
31581     
31582     onUploaderClick : function(e)
31583     {
31584         e.preventDefault();
31585      
31586         if(this.fireEvent('beforeselectfile', this) != false){
31587             this.selectorEl.dom.click();
31588         }
31589         
31590     },
31591     
31592     onFileSelected : function(e)
31593     {
31594         e.preventDefault();
31595         
31596         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31597             return;
31598         }
31599         
31600         Roo.each(this.selectorEl.dom.files, function(file){
31601             if(this.fireEvent('inspect', this, file) != false){
31602                 this.files.push(file);
31603             }
31604         }, this);
31605         
31606         this.queue();
31607         
31608     },
31609     
31610     queue : function()
31611     {
31612         this.selectorEl.dom.value = '';
31613         
31614         if(!this.files || !this.files.length){
31615             return;
31616         }
31617         
31618         if(this.boxes > 0 && this.files.length > this.boxes){
31619             this.files = this.files.slice(0, this.boxes);
31620         }
31621         
31622         this.uploader.show();
31623         
31624         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31625             this.uploader.hide();
31626         }
31627         
31628         var _this = this;
31629         
31630         var files = [];
31631         
31632         var docs = [];
31633         
31634         Roo.each(this.files, function(file){
31635             
31636             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31637                 var f = this.renderPreview(file);
31638                 files.push(f);
31639                 return;
31640             }
31641             
31642             if(file.type.indexOf('image') != -1){
31643                 this.delegates.push(
31644                     (function(){
31645                         _this.process(file);
31646                     }).createDelegate(this)
31647                 );
31648         
31649                 return;
31650             }
31651             
31652             docs.push(
31653                 (function(){
31654                     _this.process(file);
31655                 }).createDelegate(this)
31656             );
31657             
31658         }, this);
31659         
31660         this.files = files;
31661         
31662         this.delegates = this.delegates.concat(docs);
31663         
31664         if(!this.delegates.length){
31665             this.refresh();
31666             return;
31667         }
31668         
31669         this.progressBar.aria_valuemax = this.delegates.length;
31670         
31671         this.arrange();
31672         
31673         return;
31674     },
31675     
31676     arrange : function()
31677     {
31678         if(!this.delegates.length){
31679             this.progressDialog.hide();
31680             this.refresh();
31681             return;
31682         }
31683         
31684         var delegate = this.delegates.shift();
31685         
31686         this.progressDialog.show();
31687         
31688         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31689         
31690         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31691         
31692         delegate();
31693     },
31694     
31695     refresh : function()
31696     {
31697         this.uploader.show();
31698         
31699         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31700             this.uploader.hide();
31701         }
31702         
31703         Roo.isTouch ? this.closable(false) : this.closable(true);
31704         
31705         this.fireEvent('refresh', this);
31706     },
31707     
31708     onRemove : function(e, el, o)
31709     {
31710         e.preventDefault();
31711         
31712         this.fireEvent('remove', this, o);
31713         
31714     },
31715     
31716     remove : function(o)
31717     {
31718         var files = [];
31719         
31720         Roo.each(this.files, function(file){
31721             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31722                 files.push(file);
31723                 return;
31724             }
31725
31726             o.target.remove();
31727
31728         }, this);
31729         
31730         this.files = files;
31731         
31732         this.refresh();
31733     },
31734     
31735     clear : function()
31736     {
31737         Roo.each(this.files, function(file){
31738             if(!file.target){
31739                 return;
31740             }
31741             
31742             file.target.remove();
31743
31744         }, this);
31745         
31746         this.files = [];
31747         
31748         this.refresh();
31749     },
31750     
31751     onClick : function(e, el, o)
31752     {
31753         e.preventDefault();
31754         
31755         this.fireEvent('click', this, o);
31756         
31757     },
31758     
31759     closable : function(closable)
31760     {
31761         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31762             
31763             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31764             
31765             if(closable){
31766                 el.show();
31767                 return;
31768             }
31769             
31770             el.hide();
31771             
31772         }, this);
31773     },
31774     
31775     xhrOnLoad : function(xhr)
31776     {
31777         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31778             el.remove();
31779         }, this);
31780         
31781         if (xhr.readyState !== 4) {
31782             this.arrange();
31783             this.fireEvent('exception', this, xhr);
31784             return;
31785         }
31786
31787         var response = Roo.decode(xhr.responseText);
31788         
31789         if(!response.success){
31790             this.arrange();
31791             this.fireEvent('exception', this, xhr);
31792             return;
31793         }
31794         
31795         var file = this.renderPreview(response.data);
31796         
31797         this.files.push(file);
31798         
31799         this.arrange();
31800         
31801         this.fireEvent('afterupload', this, xhr);
31802         
31803     },
31804     
31805     xhrOnError : function(xhr)
31806     {
31807         Roo.log('xhr on error');
31808         
31809         var response = Roo.decode(xhr.responseText);
31810           
31811         Roo.log(response);
31812         
31813         this.arrange();
31814     },
31815     
31816     process : function(file)
31817     {
31818         if(this.fireEvent('process', this, file) !== false){
31819             if(this.editable && file.type.indexOf('image') != -1){
31820                 this.fireEvent('edit', this, file);
31821                 return;
31822             }
31823
31824             this.uploadStart(file, false);
31825
31826             return;
31827         }
31828         
31829     },
31830     
31831     uploadStart : function(file, crop)
31832     {
31833         this.xhr = new XMLHttpRequest();
31834         
31835         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31836             this.arrange();
31837             return;
31838         }
31839         
31840         file.xhr = this.xhr;
31841             
31842         this.managerEl.createChild({
31843             tag : 'div',
31844             cls : 'roo-document-manager-loading',
31845             cn : [
31846                 {
31847                     tag : 'div',
31848                     tooltip : file.name,
31849                     cls : 'roo-document-manager-thumb',
31850                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31851                 }
31852             ]
31853
31854         });
31855
31856         this.xhr.open(this.method, this.url, true);
31857         
31858         var headers = {
31859             "Accept": "application/json",
31860             "Cache-Control": "no-cache",
31861             "X-Requested-With": "XMLHttpRequest"
31862         };
31863         
31864         for (var headerName in headers) {
31865             var headerValue = headers[headerName];
31866             if (headerValue) {
31867                 this.xhr.setRequestHeader(headerName, headerValue);
31868             }
31869         }
31870         
31871         var _this = this;
31872         
31873         this.xhr.onload = function()
31874         {
31875             _this.xhrOnLoad(_this.xhr);
31876         }
31877         
31878         this.xhr.onerror = function()
31879         {
31880             _this.xhrOnError(_this.xhr);
31881         }
31882         
31883         var formData = new FormData();
31884
31885         formData.append('returnHTML', 'NO');
31886         
31887         if(crop){
31888             formData.append('crop', crop);
31889         }
31890         
31891         formData.append(this.paramName, file, file.name);
31892         
31893         var options = {
31894             file : file, 
31895             manually : false
31896         };
31897         
31898         if(this.fireEvent('prepare', this, formData, options) != false){
31899             
31900             if(options.manually){
31901                 return;
31902             }
31903             
31904             this.xhr.send(formData);
31905             return;
31906         };
31907         
31908         this.uploadCancel();
31909     },
31910     
31911     uploadCancel : function()
31912     {
31913         if (this.xhr) {
31914             this.xhr.abort();
31915         }
31916         
31917         this.delegates = [];
31918         
31919         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31920             el.remove();
31921         }, this);
31922         
31923         this.arrange();
31924     },
31925     
31926     renderPreview : function(file)
31927     {
31928         if(typeof(file.target) != 'undefined' && file.target){
31929             return file;
31930         }
31931         
31932         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31933         
31934         var previewEl = this.managerEl.createChild({
31935             tag : 'div',
31936             cls : 'roo-document-manager-preview',
31937             cn : [
31938                 {
31939                     tag : 'div',
31940                     tooltip : file[this.toolTipName],
31941                     cls : 'roo-document-manager-thumb',
31942                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31943                 },
31944                 {
31945                     tag : 'button',
31946                     cls : 'close',
31947                     html : '<i class="fa fa-times-circle"></i>'
31948                 }
31949             ]
31950         });
31951
31952         var close = previewEl.select('button.close', true).first();
31953
31954         close.on('click', this.onRemove, this, file);
31955
31956         file.target = previewEl;
31957
31958         var image = previewEl.select('img', true).first();
31959         
31960         var _this = this;
31961         
31962         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31963         
31964         image.on('click', this.onClick, this, file);
31965         
31966         this.fireEvent('previewrendered', this, file);
31967         
31968         return file;
31969         
31970     },
31971     
31972     onPreviewLoad : function(file, image)
31973     {
31974         if(typeof(file.target) == 'undefined' || !file.target){
31975             return;
31976         }
31977         
31978         var width = image.dom.naturalWidth || image.dom.width;
31979         var height = image.dom.naturalHeight || image.dom.height;
31980         
31981         if(!this.previewResize) {
31982             return;
31983         }
31984         
31985         if(width > height){
31986             file.target.addClass('wide');
31987             return;
31988         }
31989         
31990         file.target.addClass('tall');
31991         return;
31992         
31993     },
31994     
31995     uploadFromSource : function(file, crop)
31996     {
31997         this.xhr = new XMLHttpRequest();
31998         
31999         this.managerEl.createChild({
32000             tag : 'div',
32001             cls : 'roo-document-manager-loading',
32002             cn : [
32003                 {
32004                     tag : 'div',
32005                     tooltip : file.name,
32006                     cls : 'roo-document-manager-thumb',
32007                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32008                 }
32009             ]
32010
32011         });
32012
32013         this.xhr.open(this.method, this.url, true);
32014         
32015         var headers = {
32016             "Accept": "application/json",
32017             "Cache-Control": "no-cache",
32018             "X-Requested-With": "XMLHttpRequest"
32019         };
32020         
32021         for (var headerName in headers) {
32022             var headerValue = headers[headerName];
32023             if (headerValue) {
32024                 this.xhr.setRequestHeader(headerName, headerValue);
32025             }
32026         }
32027         
32028         var _this = this;
32029         
32030         this.xhr.onload = function()
32031         {
32032             _this.xhrOnLoad(_this.xhr);
32033         }
32034         
32035         this.xhr.onerror = function()
32036         {
32037             _this.xhrOnError(_this.xhr);
32038         }
32039         
32040         var formData = new FormData();
32041
32042         formData.append('returnHTML', 'NO');
32043         
32044         formData.append('crop', crop);
32045         
32046         if(typeof(file.filename) != 'undefined'){
32047             formData.append('filename', file.filename);
32048         }
32049         
32050         if(typeof(file.mimetype) != 'undefined'){
32051             formData.append('mimetype', file.mimetype);
32052         }
32053         
32054         Roo.log(formData);
32055         
32056         if(this.fireEvent('prepare', this, formData) != false){
32057             this.xhr.send(formData);
32058         };
32059     }
32060 });
32061
32062 /*
32063 * Licence: LGPL
32064 */
32065
32066 /**
32067  * @class Roo.bootstrap.DocumentViewer
32068  * @extends Roo.bootstrap.Component
32069  * Bootstrap DocumentViewer class
32070  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32071  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32072  * 
32073  * @constructor
32074  * Create a new DocumentViewer
32075  * @param {Object} config The config object
32076  */
32077
32078 Roo.bootstrap.DocumentViewer = function(config){
32079     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32080     
32081     this.addEvents({
32082         /**
32083          * @event initial
32084          * Fire after initEvent
32085          * @param {Roo.bootstrap.DocumentViewer} this
32086          */
32087         "initial" : true,
32088         /**
32089          * @event click
32090          * Fire after click
32091          * @param {Roo.bootstrap.DocumentViewer} this
32092          */
32093         "click" : true,
32094         /**
32095          * @event download
32096          * Fire after download button
32097          * @param {Roo.bootstrap.DocumentViewer} this
32098          */
32099         "download" : true,
32100         /**
32101          * @event trash
32102          * Fire after trash button
32103          * @param {Roo.bootstrap.DocumentViewer} this
32104          */
32105         "trash" : true
32106         
32107     });
32108 };
32109
32110 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32111     
32112     showDownload : true,
32113     
32114     showTrash : true,
32115     
32116     getAutoCreate : function()
32117     {
32118         var cfg = {
32119             tag : 'div',
32120             cls : 'roo-document-viewer',
32121             cn : [
32122                 {
32123                     tag : 'div',
32124                     cls : 'roo-document-viewer-body',
32125                     cn : [
32126                         {
32127                             tag : 'div',
32128                             cls : 'roo-document-viewer-thumb',
32129                             cn : [
32130                                 {
32131                                     tag : 'img',
32132                                     cls : 'roo-document-viewer-image'
32133                                 }
32134                             ]
32135                         }
32136                     ]
32137                 },
32138                 {
32139                     tag : 'div',
32140                     cls : 'roo-document-viewer-footer',
32141                     cn : {
32142                         tag : 'div',
32143                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32144                         cn : [
32145                             {
32146                                 tag : 'div',
32147                                 cls : 'btn-group roo-document-viewer-download',
32148                                 cn : [
32149                                     {
32150                                         tag : 'button',
32151                                         cls : 'btn btn-default',
32152                                         html : '<i class="fa fa-download"></i>'
32153                                     }
32154                                 ]
32155                             },
32156                             {
32157                                 tag : 'div',
32158                                 cls : 'btn-group roo-document-viewer-trash',
32159                                 cn : [
32160                                     {
32161                                         tag : 'button',
32162                                         cls : 'btn btn-default',
32163                                         html : '<i class="fa fa-trash"></i>'
32164                                     }
32165                                 ]
32166                             }
32167                         ]
32168                     }
32169                 }
32170             ]
32171         };
32172         
32173         return cfg;
32174     },
32175     
32176     initEvents : function()
32177     {
32178         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32179         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32180         
32181         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32182         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32183         
32184         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32185         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32186         
32187         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32188         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32189         
32190         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32191         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32192         
32193         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32194         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32195         
32196         this.bodyEl.on('click', this.onClick, this);
32197         this.downloadBtn.on('click', this.onDownload, this);
32198         this.trashBtn.on('click', this.onTrash, this);
32199         
32200         this.downloadBtn.hide();
32201         this.trashBtn.hide();
32202         
32203         if(this.showDownload){
32204             this.downloadBtn.show();
32205         }
32206         
32207         if(this.showTrash){
32208             this.trashBtn.show();
32209         }
32210         
32211         if(!this.showDownload && !this.showTrash) {
32212             this.footerEl.hide();
32213         }
32214         
32215     },
32216     
32217     initial : function()
32218     {
32219         this.fireEvent('initial', this);
32220         
32221     },
32222     
32223     onClick : function(e)
32224     {
32225         e.preventDefault();
32226         
32227         this.fireEvent('click', this);
32228     },
32229     
32230     onDownload : function(e)
32231     {
32232         e.preventDefault();
32233         
32234         this.fireEvent('download', this);
32235     },
32236     
32237     onTrash : function(e)
32238     {
32239         e.preventDefault();
32240         
32241         this.fireEvent('trash', this);
32242     }
32243     
32244 });
32245 /*
32246  * - LGPL
32247  *
32248  * nav progress bar
32249  * 
32250  */
32251
32252 /**
32253  * @class Roo.bootstrap.NavProgressBar
32254  * @extends Roo.bootstrap.Component
32255  * Bootstrap NavProgressBar class
32256  * 
32257  * @constructor
32258  * Create a new nav progress bar
32259  * @param {Object} config The config object
32260  */
32261
32262 Roo.bootstrap.NavProgressBar = function(config){
32263     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32264
32265     this.bullets = this.bullets || [];
32266    
32267 //    Roo.bootstrap.NavProgressBar.register(this);
32268      this.addEvents({
32269         /**
32270              * @event changed
32271              * Fires when the active item changes
32272              * @param {Roo.bootstrap.NavProgressBar} this
32273              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32274              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32275          */
32276         'changed': true
32277      });
32278     
32279 };
32280
32281 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32282     
32283     bullets : [],
32284     barItems : [],
32285     
32286     getAutoCreate : function()
32287     {
32288         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32289         
32290         cfg = {
32291             tag : 'div',
32292             cls : 'roo-navigation-bar-group',
32293             cn : [
32294                 {
32295                     tag : 'div',
32296                     cls : 'roo-navigation-top-bar'
32297                 },
32298                 {
32299                     tag : 'div',
32300                     cls : 'roo-navigation-bullets-bar',
32301                     cn : [
32302                         {
32303                             tag : 'ul',
32304                             cls : 'roo-navigation-bar'
32305                         }
32306                     ]
32307                 },
32308                 
32309                 {
32310                     tag : 'div',
32311                     cls : 'roo-navigation-bottom-bar'
32312                 }
32313             ]
32314             
32315         };
32316         
32317         return cfg;
32318         
32319     },
32320     
32321     initEvents: function() 
32322     {
32323         
32324     },
32325     
32326     onRender : function(ct, position) 
32327     {
32328         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32329         
32330         if(this.bullets.length){
32331             Roo.each(this.bullets, function(b){
32332                this.addItem(b);
32333             }, this);
32334         }
32335         
32336         this.format();
32337         
32338     },
32339     
32340     addItem : function(cfg)
32341     {
32342         var item = new Roo.bootstrap.NavProgressItem(cfg);
32343         
32344         item.parentId = this.id;
32345         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32346         
32347         if(cfg.html){
32348             var top = new Roo.bootstrap.Element({
32349                 tag : 'div',
32350                 cls : 'roo-navigation-bar-text'
32351             });
32352             
32353             var bottom = new Roo.bootstrap.Element({
32354                 tag : 'div',
32355                 cls : 'roo-navigation-bar-text'
32356             });
32357             
32358             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32359             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32360             
32361             var topText = new Roo.bootstrap.Element({
32362                 tag : 'span',
32363                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32364             });
32365             
32366             var bottomText = new Roo.bootstrap.Element({
32367                 tag : 'span',
32368                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32369             });
32370             
32371             topText.onRender(top.el, null);
32372             bottomText.onRender(bottom.el, null);
32373             
32374             item.topEl = top;
32375             item.bottomEl = bottom;
32376         }
32377         
32378         this.barItems.push(item);
32379         
32380         return item;
32381     },
32382     
32383     getActive : function()
32384     {
32385         var active = false;
32386         
32387         Roo.each(this.barItems, function(v){
32388             
32389             if (!v.isActive()) {
32390                 return;
32391             }
32392             
32393             active = v;
32394             return false;
32395             
32396         });
32397         
32398         return active;
32399     },
32400     
32401     setActiveItem : function(item)
32402     {
32403         var prev = false;
32404         
32405         Roo.each(this.barItems, function(v){
32406             if (v.rid == item.rid) {
32407                 return ;
32408             }
32409             
32410             if (v.isActive()) {
32411                 v.setActive(false);
32412                 prev = v;
32413             }
32414         });
32415
32416         item.setActive(true);
32417         
32418         this.fireEvent('changed', this, item, prev);
32419     },
32420     
32421     getBarItem: function(rid)
32422     {
32423         var ret = false;
32424         
32425         Roo.each(this.barItems, function(e) {
32426             if (e.rid != rid) {
32427                 return;
32428             }
32429             
32430             ret =  e;
32431             return false;
32432         });
32433         
32434         return ret;
32435     },
32436     
32437     indexOfItem : function(item)
32438     {
32439         var index = false;
32440         
32441         Roo.each(this.barItems, function(v, i){
32442             
32443             if (v.rid != item.rid) {
32444                 return;
32445             }
32446             
32447             index = i;
32448             return false
32449         });
32450         
32451         return index;
32452     },
32453     
32454     setActiveNext : function()
32455     {
32456         var i = this.indexOfItem(this.getActive());
32457         
32458         if (i > this.barItems.length) {
32459             return;
32460         }
32461         
32462         this.setActiveItem(this.barItems[i+1]);
32463     },
32464     
32465     setActivePrev : function()
32466     {
32467         var i = this.indexOfItem(this.getActive());
32468         
32469         if (i  < 1) {
32470             return;
32471         }
32472         
32473         this.setActiveItem(this.barItems[i-1]);
32474     },
32475     
32476     format : function()
32477     {
32478         if(!this.barItems.length){
32479             return;
32480         }
32481      
32482         var width = 100 / this.barItems.length;
32483         
32484         Roo.each(this.barItems, function(i){
32485             i.el.setStyle('width', width + '%');
32486             i.topEl.el.setStyle('width', width + '%');
32487             i.bottomEl.el.setStyle('width', width + '%');
32488         }, this);
32489         
32490     }
32491     
32492 });
32493 /*
32494  * - LGPL
32495  *
32496  * Nav Progress Item
32497  * 
32498  */
32499
32500 /**
32501  * @class Roo.bootstrap.NavProgressItem
32502  * @extends Roo.bootstrap.Component
32503  * Bootstrap NavProgressItem class
32504  * @cfg {String} rid the reference id
32505  * @cfg {Boolean} active (true|false) Is item active default false
32506  * @cfg {Boolean} disabled (true|false) Is item active default false
32507  * @cfg {String} html
32508  * @cfg {String} position (top|bottom) text position default bottom
32509  * @cfg {String} icon show icon instead of number
32510  * 
32511  * @constructor
32512  * Create a new NavProgressItem
32513  * @param {Object} config The config object
32514  */
32515 Roo.bootstrap.NavProgressItem = function(config){
32516     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32517     this.addEvents({
32518         // raw events
32519         /**
32520          * @event click
32521          * The raw click event for the entire grid.
32522          * @param {Roo.bootstrap.NavProgressItem} this
32523          * @param {Roo.EventObject} e
32524          */
32525         "click" : true
32526     });
32527    
32528 };
32529
32530 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32531     
32532     rid : '',
32533     active : false,
32534     disabled : false,
32535     html : '',
32536     position : 'bottom',
32537     icon : false,
32538     
32539     getAutoCreate : function()
32540     {
32541         var iconCls = 'roo-navigation-bar-item-icon';
32542         
32543         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32544         
32545         var cfg = {
32546             tag: 'li',
32547             cls: 'roo-navigation-bar-item',
32548             cn : [
32549                 {
32550                     tag : 'i',
32551                     cls : iconCls
32552                 }
32553             ]
32554         };
32555         
32556         if(this.active){
32557             cfg.cls += ' active';
32558         }
32559         if(this.disabled){
32560             cfg.cls += ' disabled';
32561         }
32562         
32563         return cfg;
32564     },
32565     
32566     disable : function()
32567     {
32568         this.setDisabled(true);
32569     },
32570     
32571     enable : function()
32572     {
32573         this.setDisabled(false);
32574     },
32575     
32576     initEvents: function() 
32577     {
32578         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32579         
32580         this.iconEl.on('click', this.onClick, this);
32581     },
32582     
32583     onClick : function(e)
32584     {
32585         e.preventDefault();
32586         
32587         if(this.disabled){
32588             return;
32589         }
32590         
32591         if(this.fireEvent('click', this, e) === false){
32592             return;
32593         };
32594         
32595         this.parent().setActiveItem(this);
32596     },
32597     
32598     isActive: function () 
32599     {
32600         return this.active;
32601     },
32602     
32603     setActive : function(state)
32604     {
32605         if(this.active == state){
32606             return;
32607         }
32608         
32609         this.active = state;
32610         
32611         if (state) {
32612             this.el.addClass('active');
32613             return;
32614         }
32615         
32616         this.el.removeClass('active');
32617         
32618         return;
32619     },
32620     
32621     setDisabled : function(state)
32622     {
32623         if(this.disabled == state){
32624             return;
32625         }
32626         
32627         this.disabled = state;
32628         
32629         if (state) {
32630             this.el.addClass('disabled');
32631             return;
32632         }
32633         
32634         this.el.removeClass('disabled');
32635     },
32636     
32637     tooltipEl : function()
32638     {
32639         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32640     }
32641 });
32642  
32643
32644  /*
32645  * - LGPL
32646  *
32647  * FieldLabel
32648  * 
32649  */
32650
32651 /**
32652  * @class Roo.bootstrap.FieldLabel
32653  * @extends Roo.bootstrap.Component
32654  * Bootstrap FieldLabel class
32655  * @cfg {String} html contents of the element
32656  * @cfg {String} tag tag of the element default label
32657  * @cfg {String} cls class of the element
32658  * @cfg {String} target label target 
32659  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32660  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32661  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32662  * @cfg {String} iconTooltip default "This field is required"
32663  * @cfg {String} indicatorpos (left|right) default left
32664  * 
32665  * @constructor
32666  * Create a new FieldLabel
32667  * @param {Object} config The config object
32668  */
32669
32670 Roo.bootstrap.FieldLabel = function(config){
32671     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32672     
32673     this.addEvents({
32674             /**
32675              * @event invalid
32676              * Fires after the field has been marked as invalid.
32677              * @param {Roo.form.FieldLabel} this
32678              * @param {String} msg The validation message
32679              */
32680             invalid : true,
32681             /**
32682              * @event valid
32683              * Fires after the field has been validated with no errors.
32684              * @param {Roo.form.FieldLabel} this
32685              */
32686             valid : true
32687         });
32688 };
32689
32690 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32691     
32692     tag: 'label',
32693     cls: '',
32694     html: '',
32695     target: '',
32696     allowBlank : true,
32697     invalidClass : 'has-warning',
32698     validClass : 'has-success',
32699     iconTooltip : 'This field is required',
32700     indicatorpos : 'left',
32701     
32702     getAutoCreate : function(){
32703         
32704         var cls = "";
32705         if (!this.allowBlank) {
32706             cls  = "visible";
32707         }
32708         
32709         var cfg = {
32710             tag : this.tag,
32711             cls : 'roo-bootstrap-field-label ' + this.cls,
32712             for : this.target,
32713             cn : [
32714                 {
32715                     tag : 'i',
32716                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32717                     tooltip : this.iconTooltip
32718                 },
32719                 {
32720                     tag : 'span',
32721                     html : this.html
32722                 }
32723             ] 
32724         };
32725         
32726         if(this.indicatorpos == 'right'){
32727             var cfg = {
32728                 tag : this.tag,
32729                 cls : 'roo-bootstrap-field-label ' + this.cls,
32730                 for : this.target,
32731                 cn : [
32732                     {
32733                         tag : 'span',
32734                         html : this.html
32735                     },
32736                     {
32737                         tag : 'i',
32738                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32739                         tooltip : this.iconTooltip
32740                     }
32741                 ] 
32742             };
32743         }
32744         
32745         return cfg;
32746     },
32747     
32748     initEvents: function() 
32749     {
32750         Roo.bootstrap.Element.superclass.initEvents.call(this);
32751         
32752         this.indicator = this.indicatorEl();
32753         
32754         if(this.indicator){
32755             this.indicator.removeClass('visible');
32756             this.indicator.addClass('invisible');
32757         }
32758         
32759         Roo.bootstrap.FieldLabel.register(this);
32760     },
32761     
32762     indicatorEl : function()
32763     {
32764         var indicator = this.el.select('i.roo-required-indicator',true).first();
32765         
32766         if(!indicator){
32767             return false;
32768         }
32769         
32770         return indicator;
32771         
32772     },
32773     
32774     /**
32775      * Mark this field as valid
32776      */
32777     markValid : function()
32778     {
32779         if(this.indicator){
32780             this.indicator.removeClass('visible');
32781             this.indicator.addClass('invisible');
32782         }
32783         if (Roo.bootstrap.version == 3) {
32784             this.el.removeClass(this.invalidClass);
32785             this.el.addClass(this.validClass);
32786         } else {
32787             this.el.removeClass('is-invalid');
32788             this.el.addClass('is-valid');
32789         }
32790         
32791         
32792         this.fireEvent('valid', this);
32793     },
32794     
32795     /**
32796      * Mark this field as invalid
32797      * @param {String} msg The validation message
32798      */
32799     markInvalid : function(msg)
32800     {
32801         if(this.indicator){
32802             this.indicator.removeClass('invisible');
32803             this.indicator.addClass('visible');
32804         }
32805           if (Roo.bootstrap.version == 3) {
32806             this.el.removeClass(this.validClass);
32807             this.el.addClass(this.invalidClass);
32808         } else {
32809             this.el.removeClass('is-valid');
32810             this.el.addClass('is-invalid');
32811         }
32812         
32813         
32814         this.fireEvent('invalid', this, msg);
32815     }
32816     
32817    
32818 });
32819
32820 Roo.apply(Roo.bootstrap.FieldLabel, {
32821     
32822     groups: {},
32823     
32824      /**
32825     * register a FieldLabel Group
32826     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32827     */
32828     register : function(label)
32829     {
32830         if(this.groups.hasOwnProperty(label.target)){
32831             return;
32832         }
32833      
32834         this.groups[label.target] = label;
32835         
32836     },
32837     /**
32838     * fetch a FieldLabel Group based on the target
32839     * @param {string} target
32840     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32841     */
32842     get: function(target) {
32843         if (typeof(this.groups[target]) == 'undefined') {
32844             return false;
32845         }
32846         
32847         return this.groups[target] ;
32848     }
32849 });
32850
32851  
32852
32853  /*
32854  * - LGPL
32855  *
32856  * page DateSplitField.
32857  * 
32858  */
32859
32860
32861 /**
32862  * @class Roo.bootstrap.DateSplitField
32863  * @extends Roo.bootstrap.Component
32864  * Bootstrap DateSplitField class
32865  * @cfg {string} fieldLabel - the label associated
32866  * @cfg {Number} labelWidth set the width of label (0-12)
32867  * @cfg {String} labelAlign (top|left)
32868  * @cfg {Boolean} dayAllowBlank (true|false) default false
32869  * @cfg {Boolean} monthAllowBlank (true|false) default false
32870  * @cfg {Boolean} yearAllowBlank (true|false) default false
32871  * @cfg {string} dayPlaceholder 
32872  * @cfg {string} monthPlaceholder
32873  * @cfg {string} yearPlaceholder
32874  * @cfg {string} dayFormat default 'd'
32875  * @cfg {string} monthFormat default 'm'
32876  * @cfg {string} yearFormat default 'Y'
32877  * @cfg {Number} labellg set the width of label (1-12)
32878  * @cfg {Number} labelmd set the width of label (1-12)
32879  * @cfg {Number} labelsm set the width of label (1-12)
32880  * @cfg {Number} labelxs set the width of label (1-12)
32881
32882  *     
32883  * @constructor
32884  * Create a new DateSplitField
32885  * @param {Object} config The config object
32886  */
32887
32888 Roo.bootstrap.DateSplitField = function(config){
32889     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32890     
32891     this.addEvents({
32892         // raw events
32893          /**
32894          * @event years
32895          * getting the data of years
32896          * @param {Roo.bootstrap.DateSplitField} this
32897          * @param {Object} years
32898          */
32899         "years" : true,
32900         /**
32901          * @event days
32902          * getting the data of days
32903          * @param {Roo.bootstrap.DateSplitField} this
32904          * @param {Object} days
32905          */
32906         "days" : true,
32907         /**
32908          * @event invalid
32909          * Fires after the field has been marked as invalid.
32910          * @param {Roo.form.Field} this
32911          * @param {String} msg The validation message
32912          */
32913         invalid : true,
32914        /**
32915          * @event valid
32916          * Fires after the field has been validated with no errors.
32917          * @param {Roo.form.Field} this
32918          */
32919         valid : true
32920     });
32921 };
32922
32923 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32924     
32925     fieldLabel : '',
32926     labelAlign : 'top',
32927     labelWidth : 3,
32928     dayAllowBlank : false,
32929     monthAllowBlank : false,
32930     yearAllowBlank : false,
32931     dayPlaceholder : '',
32932     monthPlaceholder : '',
32933     yearPlaceholder : '',
32934     dayFormat : 'd',
32935     monthFormat : 'm',
32936     yearFormat : 'Y',
32937     isFormField : true,
32938     labellg : 0,
32939     labelmd : 0,
32940     labelsm : 0,
32941     labelxs : 0,
32942     
32943     getAutoCreate : function()
32944     {
32945         var cfg = {
32946             tag : 'div',
32947             cls : 'row roo-date-split-field-group',
32948             cn : [
32949                 {
32950                     tag : 'input',
32951                     type : 'hidden',
32952                     cls : 'form-hidden-field roo-date-split-field-group-value',
32953                     name : this.name
32954                 }
32955             ]
32956         };
32957         
32958         var labelCls = 'col-md-12';
32959         var contentCls = 'col-md-4';
32960         
32961         if(this.fieldLabel){
32962             
32963             var label = {
32964                 tag : 'div',
32965                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32966                 cn : [
32967                     {
32968                         tag : 'label',
32969                         html : this.fieldLabel
32970                     }
32971                 ]
32972             };
32973             
32974             if(this.labelAlign == 'left'){
32975             
32976                 if(this.labelWidth > 12){
32977                     label.style = "width: " + this.labelWidth + 'px';
32978                 }
32979
32980                 if(this.labelWidth < 13 && this.labelmd == 0){
32981                     this.labelmd = this.labelWidth;
32982                 }
32983
32984                 if(this.labellg > 0){
32985                     labelCls = ' col-lg-' + this.labellg;
32986                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32987                 }
32988
32989                 if(this.labelmd > 0){
32990                     labelCls = ' col-md-' + this.labelmd;
32991                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32992                 }
32993
32994                 if(this.labelsm > 0){
32995                     labelCls = ' col-sm-' + this.labelsm;
32996                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32997                 }
32998
32999                 if(this.labelxs > 0){
33000                     labelCls = ' col-xs-' + this.labelxs;
33001                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33002                 }
33003             }
33004             
33005             label.cls += ' ' + labelCls;
33006             
33007             cfg.cn.push(label);
33008         }
33009         
33010         Roo.each(['day', 'month', 'year'], function(t){
33011             cfg.cn.push({
33012                 tag : 'div',
33013                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33014             });
33015         }, this);
33016         
33017         return cfg;
33018     },
33019     
33020     inputEl: function ()
33021     {
33022         return this.el.select('.roo-date-split-field-group-value', true).first();
33023     },
33024     
33025     onRender : function(ct, position) 
33026     {
33027         var _this = this;
33028         
33029         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33030         
33031         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33032         
33033         this.dayField = new Roo.bootstrap.ComboBox({
33034             allowBlank : this.dayAllowBlank,
33035             alwaysQuery : true,
33036             displayField : 'value',
33037             editable : false,
33038             fieldLabel : '',
33039             forceSelection : true,
33040             mode : 'local',
33041             placeholder : this.dayPlaceholder,
33042             selectOnFocus : true,
33043             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33044             triggerAction : 'all',
33045             typeAhead : true,
33046             valueField : 'value',
33047             store : new Roo.data.SimpleStore({
33048                 data : (function() {    
33049                     var days = [];
33050                     _this.fireEvent('days', _this, days);
33051                     return days;
33052                 })(),
33053                 fields : [ 'value' ]
33054             }),
33055             listeners : {
33056                 select : function (_self, record, index)
33057                 {
33058                     _this.setValue(_this.getValue());
33059                 }
33060             }
33061         });
33062
33063         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33064         
33065         this.monthField = new Roo.bootstrap.MonthField({
33066             after : '<i class=\"fa fa-calendar\"></i>',
33067             allowBlank : this.monthAllowBlank,
33068             placeholder : this.monthPlaceholder,
33069             readOnly : true,
33070             listeners : {
33071                 render : function (_self)
33072                 {
33073                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33074                         e.preventDefault();
33075                         _self.focus();
33076                     });
33077                 },
33078                 select : function (_self, oldvalue, newvalue)
33079                 {
33080                     _this.setValue(_this.getValue());
33081                 }
33082             }
33083         });
33084         
33085         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33086         
33087         this.yearField = new Roo.bootstrap.ComboBox({
33088             allowBlank : this.yearAllowBlank,
33089             alwaysQuery : true,
33090             displayField : 'value',
33091             editable : false,
33092             fieldLabel : '',
33093             forceSelection : true,
33094             mode : 'local',
33095             placeholder : this.yearPlaceholder,
33096             selectOnFocus : true,
33097             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33098             triggerAction : 'all',
33099             typeAhead : true,
33100             valueField : 'value',
33101             store : new Roo.data.SimpleStore({
33102                 data : (function() {
33103                     var years = [];
33104                     _this.fireEvent('years', _this, years);
33105                     return years;
33106                 })(),
33107                 fields : [ 'value' ]
33108             }),
33109             listeners : {
33110                 select : function (_self, record, index)
33111                 {
33112                     _this.setValue(_this.getValue());
33113                 }
33114             }
33115         });
33116
33117         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33118     },
33119     
33120     setValue : function(v, format)
33121     {
33122         this.inputEl.dom.value = v;
33123         
33124         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33125         
33126         var d = Date.parseDate(v, f);
33127         
33128         if(!d){
33129             this.validate();
33130             return;
33131         }
33132         
33133         this.setDay(d.format(this.dayFormat));
33134         this.setMonth(d.format(this.monthFormat));
33135         this.setYear(d.format(this.yearFormat));
33136         
33137         this.validate();
33138         
33139         return;
33140     },
33141     
33142     setDay : function(v)
33143     {
33144         this.dayField.setValue(v);
33145         this.inputEl.dom.value = this.getValue();
33146         this.validate();
33147         return;
33148     },
33149     
33150     setMonth : function(v)
33151     {
33152         this.monthField.setValue(v, true);
33153         this.inputEl.dom.value = this.getValue();
33154         this.validate();
33155         return;
33156     },
33157     
33158     setYear : function(v)
33159     {
33160         this.yearField.setValue(v);
33161         this.inputEl.dom.value = this.getValue();
33162         this.validate();
33163         return;
33164     },
33165     
33166     getDay : function()
33167     {
33168         return this.dayField.getValue();
33169     },
33170     
33171     getMonth : function()
33172     {
33173         return this.monthField.getValue();
33174     },
33175     
33176     getYear : function()
33177     {
33178         return this.yearField.getValue();
33179     },
33180     
33181     getValue : function()
33182     {
33183         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33184         
33185         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33186         
33187         return date;
33188     },
33189     
33190     reset : function()
33191     {
33192         this.setDay('');
33193         this.setMonth('');
33194         this.setYear('');
33195         this.inputEl.dom.value = '';
33196         this.validate();
33197         return;
33198     },
33199     
33200     validate : function()
33201     {
33202         var d = this.dayField.validate();
33203         var m = this.monthField.validate();
33204         var y = this.yearField.validate();
33205         
33206         var valid = true;
33207         
33208         if(
33209                 (!this.dayAllowBlank && !d) ||
33210                 (!this.monthAllowBlank && !m) ||
33211                 (!this.yearAllowBlank && !y)
33212         ){
33213             valid = false;
33214         }
33215         
33216         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33217             return valid;
33218         }
33219         
33220         if(valid){
33221             this.markValid();
33222             return valid;
33223         }
33224         
33225         this.markInvalid();
33226         
33227         return valid;
33228     },
33229     
33230     markValid : function()
33231     {
33232         
33233         var label = this.el.select('label', true).first();
33234         var icon = this.el.select('i.fa-star', true).first();
33235
33236         if(label && icon){
33237             icon.remove();
33238         }
33239         
33240         this.fireEvent('valid', this);
33241     },
33242     
33243      /**
33244      * Mark this field as invalid
33245      * @param {String} msg The validation message
33246      */
33247     markInvalid : function(msg)
33248     {
33249         
33250         var label = this.el.select('label', true).first();
33251         var icon = this.el.select('i.fa-star', true).first();
33252
33253         if(label && !icon){
33254             this.el.select('.roo-date-split-field-label', true).createChild({
33255                 tag : 'i',
33256                 cls : 'text-danger fa fa-lg fa-star',
33257                 tooltip : 'This field is required',
33258                 style : 'margin-right:5px;'
33259             }, label, true);
33260         }
33261         
33262         this.fireEvent('invalid', this, msg);
33263     },
33264     
33265     clearInvalid : function()
33266     {
33267         var label = this.el.select('label', true).first();
33268         var icon = this.el.select('i.fa-star', true).first();
33269
33270         if(label && icon){
33271             icon.remove();
33272         }
33273         
33274         this.fireEvent('valid', this);
33275     },
33276     
33277     getName: function()
33278     {
33279         return this.name;
33280     }
33281     
33282 });
33283
33284  /**
33285  *
33286  * This is based on 
33287  * http://masonry.desandro.com
33288  *
33289  * The idea is to render all the bricks based on vertical width...
33290  *
33291  * The original code extends 'outlayer' - we might need to use that....
33292  * 
33293  */
33294
33295
33296 /**
33297  * @class Roo.bootstrap.LayoutMasonry
33298  * @extends Roo.bootstrap.Component
33299  * Bootstrap Layout Masonry class
33300  * 
33301  * @constructor
33302  * Create a new Element
33303  * @param {Object} config The config object
33304  */
33305
33306 Roo.bootstrap.LayoutMasonry = function(config){
33307     
33308     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33309     
33310     this.bricks = [];
33311     
33312     Roo.bootstrap.LayoutMasonry.register(this);
33313     
33314     this.addEvents({
33315         // raw events
33316         /**
33317          * @event layout
33318          * Fire after layout the items
33319          * @param {Roo.bootstrap.LayoutMasonry} this
33320          * @param {Roo.EventObject} e
33321          */
33322         "layout" : true
33323     });
33324     
33325 };
33326
33327 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33328     
33329     /**
33330      * @cfg {Boolean} isLayoutInstant = no animation?
33331      */   
33332     isLayoutInstant : false, // needed?
33333    
33334     /**
33335      * @cfg {Number} boxWidth  width of the columns
33336      */   
33337     boxWidth : 450,
33338     
33339       /**
33340      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33341      */   
33342     boxHeight : 0,
33343     
33344     /**
33345      * @cfg {Number} padWidth padding below box..
33346      */   
33347     padWidth : 10, 
33348     
33349     /**
33350      * @cfg {Number} gutter gutter width..
33351      */   
33352     gutter : 10,
33353     
33354      /**
33355      * @cfg {Number} maxCols maximum number of columns
33356      */   
33357     
33358     maxCols: 0,
33359     
33360     /**
33361      * @cfg {Boolean} isAutoInitial defalut true
33362      */   
33363     isAutoInitial : true, 
33364     
33365     containerWidth: 0,
33366     
33367     /**
33368      * @cfg {Boolean} isHorizontal defalut false
33369      */   
33370     isHorizontal : false, 
33371
33372     currentSize : null,
33373     
33374     tag: 'div',
33375     
33376     cls: '',
33377     
33378     bricks: null, //CompositeElement
33379     
33380     cols : 1,
33381     
33382     _isLayoutInited : false,
33383     
33384 //    isAlternative : false, // only use for vertical layout...
33385     
33386     /**
33387      * @cfg {Number} alternativePadWidth padding below box..
33388      */   
33389     alternativePadWidth : 50,
33390     
33391     selectedBrick : [],
33392     
33393     getAutoCreate : function(){
33394         
33395         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33396         
33397         var cfg = {
33398             tag: this.tag,
33399             cls: 'blog-masonary-wrapper ' + this.cls,
33400             cn : {
33401                 cls : 'mas-boxes masonary'
33402             }
33403         };
33404         
33405         return cfg;
33406     },
33407     
33408     getChildContainer: function( )
33409     {
33410         if (this.boxesEl) {
33411             return this.boxesEl;
33412         }
33413         
33414         this.boxesEl = this.el.select('.mas-boxes').first();
33415         
33416         return this.boxesEl;
33417     },
33418     
33419     
33420     initEvents : function()
33421     {
33422         var _this = this;
33423         
33424         if(this.isAutoInitial){
33425             Roo.log('hook children rendered');
33426             this.on('childrenrendered', function() {
33427                 Roo.log('children rendered');
33428                 _this.initial();
33429             } ,this);
33430         }
33431     },
33432     
33433     initial : function()
33434     {
33435         this.selectedBrick = [];
33436         
33437         this.currentSize = this.el.getBox(true);
33438         
33439         Roo.EventManager.onWindowResize(this.resize, this); 
33440
33441         if(!this.isAutoInitial){
33442             this.layout();
33443             return;
33444         }
33445         
33446         this.layout();
33447         
33448         return;
33449         //this.layout.defer(500,this);
33450         
33451     },
33452     
33453     resize : function()
33454     {
33455         var cs = this.el.getBox(true);
33456         
33457         if (
33458                 this.currentSize.width == cs.width && 
33459                 this.currentSize.x == cs.x && 
33460                 this.currentSize.height == cs.height && 
33461                 this.currentSize.y == cs.y 
33462         ) {
33463             Roo.log("no change in with or X or Y");
33464             return;
33465         }
33466         
33467         this.currentSize = cs;
33468         
33469         this.layout();
33470         
33471     },
33472     
33473     layout : function()
33474     {   
33475         this._resetLayout();
33476         
33477         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33478         
33479         this.layoutItems( isInstant );
33480       
33481         this._isLayoutInited = true;
33482         
33483         this.fireEvent('layout', this);
33484         
33485     },
33486     
33487     _resetLayout : function()
33488     {
33489         if(this.isHorizontal){
33490             this.horizontalMeasureColumns();
33491             return;
33492         }
33493         
33494         this.verticalMeasureColumns();
33495         
33496     },
33497     
33498     verticalMeasureColumns : function()
33499     {
33500         this.getContainerWidth();
33501         
33502 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33503 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33504 //            return;
33505 //        }
33506         
33507         var boxWidth = this.boxWidth + this.padWidth;
33508         
33509         if(this.containerWidth < this.boxWidth){
33510             boxWidth = this.containerWidth
33511         }
33512         
33513         var containerWidth = this.containerWidth;
33514         
33515         var cols = Math.floor(containerWidth / boxWidth);
33516         
33517         this.cols = Math.max( cols, 1 );
33518         
33519         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33520         
33521         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33522         
33523         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33524         
33525         this.colWidth = boxWidth + avail - this.padWidth;
33526         
33527         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33528         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33529     },
33530     
33531     horizontalMeasureColumns : function()
33532     {
33533         this.getContainerWidth();
33534         
33535         var boxWidth = this.boxWidth;
33536         
33537         if(this.containerWidth < boxWidth){
33538             boxWidth = this.containerWidth;
33539         }
33540         
33541         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33542         
33543         this.el.setHeight(boxWidth);
33544         
33545     },
33546     
33547     getContainerWidth : function()
33548     {
33549         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33550     },
33551     
33552     layoutItems : function( isInstant )
33553     {
33554         Roo.log(this.bricks);
33555         
33556         var items = Roo.apply([], this.bricks);
33557         
33558         if(this.isHorizontal){
33559             this._horizontalLayoutItems( items , isInstant );
33560             return;
33561         }
33562         
33563 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33564 //            this._verticalAlternativeLayoutItems( items , isInstant );
33565 //            return;
33566 //        }
33567         
33568         this._verticalLayoutItems( items , isInstant );
33569         
33570     },
33571     
33572     _verticalLayoutItems : function ( items , isInstant)
33573     {
33574         if ( !items || !items.length ) {
33575             return;
33576         }
33577         
33578         var standard = [
33579             ['xs', 'xs', 'xs', 'tall'],
33580             ['xs', 'xs', 'tall'],
33581             ['xs', 'xs', 'sm'],
33582             ['xs', 'xs', 'xs'],
33583             ['xs', 'tall'],
33584             ['xs', 'sm'],
33585             ['xs', 'xs'],
33586             ['xs'],
33587             
33588             ['sm', 'xs', 'xs'],
33589             ['sm', 'xs'],
33590             ['sm'],
33591             
33592             ['tall', 'xs', 'xs', 'xs'],
33593             ['tall', 'xs', 'xs'],
33594             ['tall', 'xs'],
33595             ['tall']
33596             
33597         ];
33598         
33599         var queue = [];
33600         
33601         var boxes = [];
33602         
33603         var box = [];
33604         
33605         Roo.each(items, function(item, k){
33606             
33607             switch (item.size) {
33608                 // these layouts take up a full box,
33609                 case 'md' :
33610                 case 'md-left' :
33611                 case 'md-right' :
33612                 case 'wide' :
33613                     
33614                     if(box.length){
33615                         boxes.push(box);
33616                         box = [];
33617                     }
33618                     
33619                     boxes.push([item]);
33620                     
33621                     break;
33622                     
33623                 case 'xs' :
33624                 case 'sm' :
33625                 case 'tall' :
33626                     
33627                     box.push(item);
33628                     
33629                     break;
33630                 default :
33631                     break;
33632                     
33633             }
33634             
33635         }, this);
33636         
33637         if(box.length){
33638             boxes.push(box);
33639             box = [];
33640         }
33641         
33642         var filterPattern = function(box, length)
33643         {
33644             if(!box.length){
33645                 return;
33646             }
33647             
33648             var match = false;
33649             
33650             var pattern = box.slice(0, length);
33651             
33652             var format = [];
33653             
33654             Roo.each(pattern, function(i){
33655                 format.push(i.size);
33656             }, this);
33657             
33658             Roo.each(standard, function(s){
33659                 
33660                 if(String(s) != String(format)){
33661                     return;
33662                 }
33663                 
33664                 match = true;
33665                 return false;
33666                 
33667             }, this);
33668             
33669             if(!match && length == 1){
33670                 return;
33671             }
33672             
33673             if(!match){
33674                 filterPattern(box, length - 1);
33675                 return;
33676             }
33677                 
33678             queue.push(pattern);
33679
33680             box = box.slice(length, box.length);
33681
33682             filterPattern(box, 4);
33683
33684             return;
33685             
33686         }
33687         
33688         Roo.each(boxes, function(box, k){
33689             
33690             if(!box.length){
33691                 return;
33692             }
33693             
33694             if(box.length == 1){
33695                 queue.push(box);
33696                 return;
33697             }
33698             
33699             filterPattern(box, 4);
33700             
33701         }, this);
33702         
33703         this._processVerticalLayoutQueue( queue, isInstant );
33704         
33705     },
33706     
33707 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33708 //    {
33709 //        if ( !items || !items.length ) {
33710 //            return;
33711 //        }
33712 //
33713 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33714 //        
33715 //    },
33716     
33717     _horizontalLayoutItems : function ( items , isInstant)
33718     {
33719         if ( !items || !items.length || items.length < 3) {
33720             return;
33721         }
33722         
33723         items.reverse();
33724         
33725         var eItems = items.slice(0, 3);
33726         
33727         items = items.slice(3, items.length);
33728         
33729         var standard = [
33730             ['xs', 'xs', 'xs', 'wide'],
33731             ['xs', 'xs', 'wide'],
33732             ['xs', 'xs', 'sm'],
33733             ['xs', 'xs', 'xs'],
33734             ['xs', 'wide'],
33735             ['xs', 'sm'],
33736             ['xs', 'xs'],
33737             ['xs'],
33738             
33739             ['sm', 'xs', 'xs'],
33740             ['sm', 'xs'],
33741             ['sm'],
33742             
33743             ['wide', 'xs', 'xs', 'xs'],
33744             ['wide', 'xs', 'xs'],
33745             ['wide', 'xs'],
33746             ['wide'],
33747             
33748             ['wide-thin']
33749         ];
33750         
33751         var queue = [];
33752         
33753         var boxes = [];
33754         
33755         var box = [];
33756         
33757         Roo.each(items, function(item, k){
33758             
33759             switch (item.size) {
33760                 case 'md' :
33761                 case 'md-left' :
33762                 case 'md-right' :
33763                 case 'tall' :
33764                     
33765                     if(box.length){
33766                         boxes.push(box);
33767                         box = [];
33768                     }
33769                     
33770                     boxes.push([item]);
33771                     
33772                     break;
33773                     
33774                 case 'xs' :
33775                 case 'sm' :
33776                 case 'wide' :
33777                 case 'wide-thin' :
33778                     
33779                     box.push(item);
33780                     
33781                     break;
33782                 default :
33783                     break;
33784                     
33785             }
33786             
33787         }, this);
33788         
33789         if(box.length){
33790             boxes.push(box);
33791             box = [];
33792         }
33793         
33794         var filterPattern = function(box, length)
33795         {
33796             if(!box.length){
33797                 return;
33798             }
33799             
33800             var match = false;
33801             
33802             var pattern = box.slice(0, length);
33803             
33804             var format = [];
33805             
33806             Roo.each(pattern, function(i){
33807                 format.push(i.size);
33808             }, this);
33809             
33810             Roo.each(standard, function(s){
33811                 
33812                 if(String(s) != String(format)){
33813                     return;
33814                 }
33815                 
33816                 match = true;
33817                 return false;
33818                 
33819             }, this);
33820             
33821             if(!match && length == 1){
33822                 return;
33823             }
33824             
33825             if(!match){
33826                 filterPattern(box, length - 1);
33827                 return;
33828             }
33829                 
33830             queue.push(pattern);
33831
33832             box = box.slice(length, box.length);
33833
33834             filterPattern(box, 4);
33835
33836             return;
33837             
33838         }
33839         
33840         Roo.each(boxes, function(box, k){
33841             
33842             if(!box.length){
33843                 return;
33844             }
33845             
33846             if(box.length == 1){
33847                 queue.push(box);
33848                 return;
33849             }
33850             
33851             filterPattern(box, 4);
33852             
33853         }, this);
33854         
33855         
33856         var prune = [];
33857         
33858         var pos = this.el.getBox(true);
33859         
33860         var minX = pos.x;
33861         
33862         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33863         
33864         var hit_end = false;
33865         
33866         Roo.each(queue, function(box){
33867             
33868             if(hit_end){
33869                 
33870                 Roo.each(box, function(b){
33871                 
33872                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33873                     b.el.hide();
33874
33875                 }, this);
33876
33877                 return;
33878             }
33879             
33880             var mx = 0;
33881             
33882             Roo.each(box, function(b){
33883                 
33884                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33885                 b.el.show();
33886
33887                 mx = Math.max(mx, b.x);
33888                 
33889             }, this);
33890             
33891             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33892             
33893             if(maxX < minX){
33894                 
33895                 Roo.each(box, function(b){
33896                 
33897                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33898                     b.el.hide();
33899                     
33900                 }, this);
33901                 
33902                 hit_end = true;
33903                 
33904                 return;
33905             }
33906             
33907             prune.push(box);
33908             
33909         }, this);
33910         
33911         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33912     },
33913     
33914     /** Sets position of item in DOM
33915     * @param {Element} item
33916     * @param {Number} x - horizontal position
33917     * @param {Number} y - vertical position
33918     * @param {Boolean} isInstant - disables transitions
33919     */
33920     _processVerticalLayoutQueue : function( queue, isInstant )
33921     {
33922         var pos = this.el.getBox(true);
33923         var x = pos.x;
33924         var y = pos.y;
33925         var maxY = [];
33926         
33927         for (var i = 0; i < this.cols; i++){
33928             maxY[i] = pos.y;
33929         }
33930         
33931         Roo.each(queue, function(box, k){
33932             
33933             var col = k % this.cols;
33934             
33935             Roo.each(box, function(b,kk){
33936                 
33937                 b.el.position('absolute');
33938                 
33939                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33940                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33941                 
33942                 if(b.size == 'md-left' || b.size == 'md-right'){
33943                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33944                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33945                 }
33946                 
33947                 b.el.setWidth(width);
33948                 b.el.setHeight(height);
33949                 // iframe?
33950                 b.el.select('iframe',true).setSize(width,height);
33951                 
33952             }, this);
33953             
33954             for (var i = 0; i < this.cols; i++){
33955                 
33956                 if(maxY[i] < maxY[col]){
33957                     col = i;
33958                     continue;
33959                 }
33960                 
33961                 col = Math.min(col, i);
33962                 
33963             }
33964             
33965             x = pos.x + col * (this.colWidth + this.padWidth);
33966             
33967             y = maxY[col];
33968             
33969             var positions = [];
33970             
33971             switch (box.length){
33972                 case 1 :
33973                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33974                     break;
33975                 case 2 :
33976                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33977                     break;
33978                 case 3 :
33979                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33980                     break;
33981                 case 4 :
33982                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33983                     break;
33984                 default :
33985                     break;
33986             }
33987             
33988             Roo.each(box, function(b,kk){
33989                 
33990                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33991                 
33992                 var sz = b.el.getSize();
33993                 
33994                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33995                 
33996             }, this);
33997             
33998         }, this);
33999         
34000         var mY = 0;
34001         
34002         for (var i = 0; i < this.cols; i++){
34003             mY = Math.max(mY, maxY[i]);
34004         }
34005         
34006         this.el.setHeight(mY - pos.y);
34007         
34008     },
34009     
34010 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34011 //    {
34012 //        var pos = this.el.getBox(true);
34013 //        var x = pos.x;
34014 //        var y = pos.y;
34015 //        var maxX = pos.right;
34016 //        
34017 //        var maxHeight = 0;
34018 //        
34019 //        Roo.each(items, function(item, k){
34020 //            
34021 //            var c = k % 2;
34022 //            
34023 //            item.el.position('absolute');
34024 //                
34025 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34026 //
34027 //            item.el.setWidth(width);
34028 //
34029 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34030 //
34031 //            item.el.setHeight(height);
34032 //            
34033 //            if(c == 0){
34034 //                item.el.setXY([x, y], isInstant ? false : true);
34035 //            } else {
34036 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34037 //            }
34038 //            
34039 //            y = y + height + this.alternativePadWidth;
34040 //            
34041 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34042 //            
34043 //        }, this);
34044 //        
34045 //        this.el.setHeight(maxHeight);
34046 //        
34047 //    },
34048     
34049     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34050     {
34051         var pos = this.el.getBox(true);
34052         
34053         var minX = pos.x;
34054         var minY = pos.y;
34055         
34056         var maxX = pos.right;
34057         
34058         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34059         
34060         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34061         
34062         Roo.each(queue, function(box, k){
34063             
34064             Roo.each(box, function(b, kk){
34065                 
34066                 b.el.position('absolute');
34067                 
34068                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34069                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34070                 
34071                 if(b.size == 'md-left' || b.size == 'md-right'){
34072                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34073                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34074                 }
34075                 
34076                 b.el.setWidth(width);
34077                 b.el.setHeight(height);
34078                 
34079             }, this);
34080             
34081             if(!box.length){
34082                 return;
34083             }
34084             
34085             var positions = [];
34086             
34087             switch (box.length){
34088                 case 1 :
34089                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34090                     break;
34091                 case 2 :
34092                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34093                     break;
34094                 case 3 :
34095                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34096                     break;
34097                 case 4 :
34098                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34099                     break;
34100                 default :
34101                     break;
34102             }
34103             
34104             Roo.each(box, function(b,kk){
34105                 
34106                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34107                 
34108                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34109                 
34110             }, this);
34111             
34112         }, this);
34113         
34114     },
34115     
34116     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34117     {
34118         Roo.each(eItems, function(b,k){
34119             
34120             b.size = (k == 0) ? 'sm' : 'xs';
34121             b.x = (k == 0) ? 2 : 1;
34122             b.y = (k == 0) ? 2 : 1;
34123             
34124             b.el.position('absolute');
34125             
34126             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34127                 
34128             b.el.setWidth(width);
34129             
34130             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34131             
34132             b.el.setHeight(height);
34133             
34134         }, this);
34135
34136         var positions = [];
34137         
34138         positions.push({
34139             x : maxX - this.unitWidth * 2 - this.gutter,
34140             y : minY
34141         });
34142         
34143         positions.push({
34144             x : maxX - this.unitWidth,
34145             y : minY + (this.unitWidth + this.gutter) * 2
34146         });
34147         
34148         positions.push({
34149             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34150             y : minY
34151         });
34152         
34153         Roo.each(eItems, function(b,k){
34154             
34155             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34156
34157         }, this);
34158         
34159     },
34160     
34161     getVerticalOneBoxColPositions : function(x, y, box)
34162     {
34163         var pos = [];
34164         
34165         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34166         
34167         if(box[0].size == 'md-left'){
34168             rand = 0;
34169         }
34170         
34171         if(box[0].size == 'md-right'){
34172             rand = 1;
34173         }
34174         
34175         pos.push({
34176             x : x + (this.unitWidth + this.gutter) * rand,
34177             y : y
34178         });
34179         
34180         return pos;
34181     },
34182     
34183     getVerticalTwoBoxColPositions : function(x, y, box)
34184     {
34185         var pos = [];
34186         
34187         if(box[0].size == 'xs'){
34188             
34189             pos.push({
34190                 x : x,
34191                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34192             });
34193
34194             pos.push({
34195                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34196                 y : y
34197             });
34198             
34199             return pos;
34200             
34201         }
34202         
34203         pos.push({
34204             x : x,
34205             y : y
34206         });
34207
34208         pos.push({
34209             x : x + (this.unitWidth + this.gutter) * 2,
34210             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34211         });
34212         
34213         return pos;
34214         
34215     },
34216     
34217     getVerticalThreeBoxColPositions : function(x, y, box)
34218     {
34219         var pos = [];
34220         
34221         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34222             
34223             pos.push({
34224                 x : x,
34225                 y : y
34226             });
34227
34228             pos.push({
34229                 x : x + (this.unitWidth + this.gutter) * 1,
34230                 y : y
34231             });
34232             
34233             pos.push({
34234                 x : x + (this.unitWidth + this.gutter) * 2,
34235                 y : y
34236             });
34237             
34238             return pos;
34239             
34240         }
34241         
34242         if(box[0].size == 'xs' && box[1].size == 'xs'){
34243             
34244             pos.push({
34245                 x : x,
34246                 y : y
34247             });
34248
34249             pos.push({
34250                 x : x,
34251                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34252             });
34253             
34254             pos.push({
34255                 x : x + (this.unitWidth + this.gutter) * 1,
34256                 y : y
34257             });
34258             
34259             return pos;
34260             
34261         }
34262         
34263         pos.push({
34264             x : x,
34265             y : y
34266         });
34267
34268         pos.push({
34269             x : x + (this.unitWidth + this.gutter) * 2,
34270             y : y
34271         });
34272
34273         pos.push({
34274             x : x + (this.unitWidth + this.gutter) * 2,
34275             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34276         });
34277             
34278         return pos;
34279         
34280     },
34281     
34282     getVerticalFourBoxColPositions : function(x, y, box)
34283     {
34284         var pos = [];
34285         
34286         if(box[0].size == 'xs'){
34287             
34288             pos.push({
34289                 x : x,
34290                 y : y
34291             });
34292
34293             pos.push({
34294                 x : x,
34295                 y : y + (this.unitHeight + this.gutter) * 1
34296             });
34297             
34298             pos.push({
34299                 x : x,
34300                 y : y + (this.unitHeight + this.gutter) * 2
34301             });
34302             
34303             pos.push({
34304                 x : x + (this.unitWidth + this.gutter) * 1,
34305                 y : y
34306             });
34307             
34308             return pos;
34309             
34310         }
34311         
34312         pos.push({
34313             x : x,
34314             y : y
34315         });
34316
34317         pos.push({
34318             x : x + (this.unitWidth + this.gutter) * 2,
34319             y : y
34320         });
34321
34322         pos.push({
34323             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34324             y : y + (this.unitHeight + this.gutter) * 1
34325         });
34326
34327         pos.push({
34328             x : x + (this.unitWidth + this.gutter) * 2,
34329             y : y + (this.unitWidth + this.gutter) * 2
34330         });
34331
34332         return pos;
34333         
34334     },
34335     
34336     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34337     {
34338         var pos = [];
34339         
34340         if(box[0].size == 'md-left'){
34341             pos.push({
34342                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34343                 y : minY
34344             });
34345             
34346             return pos;
34347         }
34348         
34349         if(box[0].size == 'md-right'){
34350             pos.push({
34351                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34352                 y : minY + (this.unitWidth + this.gutter) * 1
34353             });
34354             
34355             return pos;
34356         }
34357         
34358         var rand = Math.floor(Math.random() * (4 - box[0].y));
34359         
34360         pos.push({
34361             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34362             y : minY + (this.unitWidth + this.gutter) * rand
34363         });
34364         
34365         return pos;
34366         
34367     },
34368     
34369     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34370     {
34371         var pos = [];
34372         
34373         if(box[0].size == 'xs'){
34374             
34375             pos.push({
34376                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34377                 y : minY
34378             });
34379
34380             pos.push({
34381                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34382                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34383             });
34384             
34385             return pos;
34386             
34387         }
34388         
34389         pos.push({
34390             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34391             y : minY
34392         });
34393
34394         pos.push({
34395             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34396             y : minY + (this.unitWidth + this.gutter) * 2
34397         });
34398         
34399         return pos;
34400         
34401     },
34402     
34403     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34404     {
34405         var pos = [];
34406         
34407         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34408             
34409             pos.push({
34410                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34411                 y : minY
34412             });
34413
34414             pos.push({
34415                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34416                 y : minY + (this.unitWidth + this.gutter) * 1
34417             });
34418             
34419             pos.push({
34420                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34421                 y : minY + (this.unitWidth + this.gutter) * 2
34422             });
34423             
34424             return pos;
34425             
34426         }
34427         
34428         if(box[0].size == 'xs' && box[1].size == 'xs'){
34429             
34430             pos.push({
34431                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34432                 y : minY
34433             });
34434
34435             pos.push({
34436                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34437                 y : minY
34438             });
34439             
34440             pos.push({
34441                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34442                 y : minY + (this.unitWidth + this.gutter) * 1
34443             });
34444             
34445             return pos;
34446             
34447         }
34448         
34449         pos.push({
34450             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34451             y : minY
34452         });
34453
34454         pos.push({
34455             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34456             y : minY + (this.unitWidth + this.gutter) * 2
34457         });
34458
34459         pos.push({
34460             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34461             y : minY + (this.unitWidth + this.gutter) * 2
34462         });
34463             
34464         return pos;
34465         
34466     },
34467     
34468     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34469     {
34470         var pos = [];
34471         
34472         if(box[0].size == 'xs'){
34473             
34474             pos.push({
34475                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34476                 y : minY
34477             });
34478
34479             pos.push({
34480                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34481                 y : minY
34482             });
34483             
34484             pos.push({
34485                 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),
34486                 y : minY
34487             });
34488             
34489             pos.push({
34490                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34491                 y : minY + (this.unitWidth + this.gutter) * 1
34492             });
34493             
34494             return pos;
34495             
34496         }
34497         
34498         pos.push({
34499             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34500             y : minY
34501         });
34502         
34503         pos.push({
34504             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34505             y : minY + (this.unitWidth + this.gutter) * 2
34506         });
34507         
34508         pos.push({
34509             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34510             y : minY + (this.unitWidth + this.gutter) * 2
34511         });
34512         
34513         pos.push({
34514             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),
34515             y : minY + (this.unitWidth + this.gutter) * 2
34516         });
34517
34518         return pos;
34519         
34520     },
34521     
34522     /**
34523     * remove a Masonry Brick
34524     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34525     */
34526     removeBrick : function(brick_id)
34527     {
34528         if (!brick_id) {
34529             return;
34530         }
34531         
34532         for (var i = 0; i<this.bricks.length; i++) {
34533             if (this.bricks[i].id == brick_id) {
34534                 this.bricks.splice(i,1);
34535                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34536                 this.initial();
34537             }
34538         }
34539     },
34540     
34541     /**
34542     * adds a Masonry Brick
34543     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34544     */
34545     addBrick : function(cfg)
34546     {
34547         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34548         //this.register(cn);
34549         cn.parentId = this.id;
34550         cn.render(this.el);
34551         return cn;
34552     },
34553     
34554     /**
34555     * register a Masonry Brick
34556     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34557     */
34558     
34559     register : function(brick)
34560     {
34561         this.bricks.push(brick);
34562         brick.masonryId = this.id;
34563     },
34564     
34565     /**
34566     * clear all the Masonry Brick
34567     */
34568     clearAll : function()
34569     {
34570         this.bricks = [];
34571         //this.getChildContainer().dom.innerHTML = "";
34572         this.el.dom.innerHTML = '';
34573     },
34574     
34575     getSelected : function()
34576     {
34577         if (!this.selectedBrick) {
34578             return false;
34579         }
34580         
34581         return this.selectedBrick;
34582     }
34583 });
34584
34585 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34586     
34587     groups: {},
34588      /**
34589     * register a Masonry Layout
34590     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34591     */
34592     
34593     register : function(layout)
34594     {
34595         this.groups[layout.id] = layout;
34596     },
34597     /**
34598     * fetch a  Masonry Layout based on the masonry layout ID
34599     * @param {string} the masonry layout to add
34600     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34601     */
34602     
34603     get: function(layout_id) {
34604         if (typeof(this.groups[layout_id]) == 'undefined') {
34605             return false;
34606         }
34607         return this.groups[layout_id] ;
34608     }
34609     
34610     
34611     
34612 });
34613
34614  
34615
34616  /**
34617  *
34618  * This is based on 
34619  * http://masonry.desandro.com
34620  *
34621  * The idea is to render all the bricks based on vertical width...
34622  *
34623  * The original code extends 'outlayer' - we might need to use that....
34624  * 
34625  */
34626
34627
34628 /**
34629  * @class Roo.bootstrap.LayoutMasonryAuto
34630  * @extends Roo.bootstrap.Component
34631  * Bootstrap Layout Masonry class
34632  * 
34633  * @constructor
34634  * Create a new Element
34635  * @param {Object} config The config object
34636  */
34637
34638 Roo.bootstrap.LayoutMasonryAuto = function(config){
34639     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34640 };
34641
34642 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34643     
34644       /**
34645      * @cfg {Boolean} isFitWidth  - resize the width..
34646      */   
34647     isFitWidth : false,  // options..
34648     /**
34649      * @cfg {Boolean} isOriginLeft = left align?
34650      */   
34651     isOriginLeft : true,
34652     /**
34653      * @cfg {Boolean} isOriginTop = top align?
34654      */   
34655     isOriginTop : false,
34656     /**
34657      * @cfg {Boolean} isLayoutInstant = no animation?
34658      */   
34659     isLayoutInstant : false, // needed?
34660     /**
34661      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34662      */   
34663     isResizingContainer : true,
34664     /**
34665      * @cfg {Number} columnWidth  width of the columns 
34666      */   
34667     
34668     columnWidth : 0,
34669     
34670     /**
34671      * @cfg {Number} maxCols maximum number of columns
34672      */   
34673     
34674     maxCols: 0,
34675     /**
34676      * @cfg {Number} padHeight padding below box..
34677      */   
34678     
34679     padHeight : 10, 
34680     
34681     /**
34682      * @cfg {Boolean} isAutoInitial defalut true
34683      */   
34684     
34685     isAutoInitial : true, 
34686     
34687     // private?
34688     gutter : 0,
34689     
34690     containerWidth: 0,
34691     initialColumnWidth : 0,
34692     currentSize : null,
34693     
34694     colYs : null, // array.
34695     maxY : 0,
34696     padWidth: 10,
34697     
34698     
34699     tag: 'div',
34700     cls: '',
34701     bricks: null, //CompositeElement
34702     cols : 0, // array?
34703     // element : null, // wrapped now this.el
34704     _isLayoutInited : null, 
34705     
34706     
34707     getAutoCreate : function(){
34708         
34709         var cfg = {
34710             tag: this.tag,
34711             cls: 'blog-masonary-wrapper ' + this.cls,
34712             cn : {
34713                 cls : 'mas-boxes masonary'
34714             }
34715         };
34716         
34717         return cfg;
34718     },
34719     
34720     getChildContainer: function( )
34721     {
34722         if (this.boxesEl) {
34723             return this.boxesEl;
34724         }
34725         
34726         this.boxesEl = this.el.select('.mas-boxes').first();
34727         
34728         return this.boxesEl;
34729     },
34730     
34731     
34732     initEvents : function()
34733     {
34734         var _this = this;
34735         
34736         if(this.isAutoInitial){
34737             Roo.log('hook children rendered');
34738             this.on('childrenrendered', function() {
34739                 Roo.log('children rendered');
34740                 _this.initial();
34741             } ,this);
34742         }
34743         
34744     },
34745     
34746     initial : function()
34747     {
34748         this.reloadItems();
34749
34750         this.currentSize = this.el.getBox(true);
34751
34752         /// was window resize... - let's see if this works..
34753         Roo.EventManager.onWindowResize(this.resize, this); 
34754
34755         if(!this.isAutoInitial){
34756             this.layout();
34757             return;
34758         }
34759         
34760         this.layout.defer(500,this);
34761     },
34762     
34763     reloadItems: function()
34764     {
34765         this.bricks = this.el.select('.masonry-brick', true);
34766         
34767         this.bricks.each(function(b) {
34768             //Roo.log(b.getSize());
34769             if (!b.attr('originalwidth')) {
34770                 b.attr('originalwidth',  b.getSize().width);
34771             }
34772             
34773         });
34774         
34775         Roo.log(this.bricks.elements.length);
34776     },
34777     
34778     resize : function()
34779     {
34780         Roo.log('resize');
34781         var cs = this.el.getBox(true);
34782         
34783         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34784             Roo.log("no change in with or X");
34785             return;
34786         }
34787         this.currentSize = cs;
34788         this.layout();
34789     },
34790     
34791     layout : function()
34792     {
34793          Roo.log('layout');
34794         this._resetLayout();
34795         //this._manageStamps();
34796       
34797         // don't animate first layout
34798         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34799         this.layoutItems( isInstant );
34800       
34801         // flag for initalized
34802         this._isLayoutInited = true;
34803     },
34804     
34805     layoutItems : function( isInstant )
34806     {
34807         //var items = this._getItemsForLayout( this.items );
34808         // original code supports filtering layout items.. we just ignore it..
34809         
34810         this._layoutItems( this.bricks , isInstant );
34811       
34812         this._postLayout();
34813     },
34814     _layoutItems : function ( items , isInstant)
34815     {
34816        //this.fireEvent( 'layout', this, items );
34817     
34818
34819         if ( !items || !items.elements.length ) {
34820           // no items, emit event with empty array
34821             return;
34822         }
34823
34824         var queue = [];
34825         items.each(function(item) {
34826             Roo.log("layout item");
34827             Roo.log(item);
34828             // get x/y object from method
34829             var position = this._getItemLayoutPosition( item );
34830             // enqueue
34831             position.item = item;
34832             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34833             queue.push( position );
34834         }, this);
34835       
34836         this._processLayoutQueue( queue );
34837     },
34838     /** Sets position of item in DOM
34839     * @param {Element} item
34840     * @param {Number} x - horizontal position
34841     * @param {Number} y - vertical position
34842     * @param {Boolean} isInstant - disables transitions
34843     */
34844     _processLayoutQueue : function( queue )
34845     {
34846         for ( var i=0, len = queue.length; i < len; i++ ) {
34847             var obj = queue[i];
34848             obj.item.position('absolute');
34849             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34850         }
34851     },
34852       
34853     
34854     /**
34855     * Any logic you want to do after each layout,
34856     * i.e. size the container
34857     */
34858     _postLayout : function()
34859     {
34860         this.resizeContainer();
34861     },
34862     
34863     resizeContainer : function()
34864     {
34865         if ( !this.isResizingContainer ) {
34866             return;
34867         }
34868         var size = this._getContainerSize();
34869         if ( size ) {
34870             this.el.setSize(size.width,size.height);
34871             this.boxesEl.setSize(size.width,size.height);
34872         }
34873     },
34874     
34875     
34876     
34877     _resetLayout : function()
34878     {
34879         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34880         this.colWidth = this.el.getWidth();
34881         //this.gutter = this.el.getWidth(); 
34882         
34883         this.measureColumns();
34884
34885         // reset column Y
34886         var i = this.cols;
34887         this.colYs = [];
34888         while (i--) {
34889             this.colYs.push( 0 );
34890         }
34891     
34892         this.maxY = 0;
34893     },
34894
34895     measureColumns : function()
34896     {
34897         this.getContainerWidth();
34898       // if columnWidth is 0, default to outerWidth of first item
34899         if ( !this.columnWidth ) {
34900             var firstItem = this.bricks.first();
34901             Roo.log(firstItem);
34902             this.columnWidth  = this.containerWidth;
34903             if (firstItem && firstItem.attr('originalwidth') ) {
34904                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34905             }
34906             // columnWidth fall back to item of first element
34907             Roo.log("set column width?");
34908                         this.initialColumnWidth = this.columnWidth  ;
34909
34910             // if first elem has no width, default to size of container
34911             
34912         }
34913         
34914         
34915         if (this.initialColumnWidth) {
34916             this.columnWidth = this.initialColumnWidth;
34917         }
34918         
34919         
34920             
34921         // column width is fixed at the top - however if container width get's smaller we should
34922         // reduce it...
34923         
34924         // this bit calcs how man columns..
34925             
34926         var columnWidth = this.columnWidth += this.gutter;
34927       
34928         // calculate columns
34929         var containerWidth = this.containerWidth + this.gutter;
34930         
34931         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34932         // fix rounding errors, typically with gutters
34933         var excess = columnWidth - containerWidth % columnWidth;
34934         
34935         
34936         // if overshoot is less than a pixel, round up, otherwise floor it
34937         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34938         cols = Math[ mathMethod ]( cols );
34939         this.cols = Math.max( cols, 1 );
34940         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34941         
34942          // padding positioning..
34943         var totalColWidth = this.cols * this.columnWidth;
34944         var padavail = this.containerWidth - totalColWidth;
34945         // so for 2 columns - we need 3 'pads'
34946         
34947         var padNeeded = (1+this.cols) * this.padWidth;
34948         
34949         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34950         
34951         this.columnWidth += padExtra
34952         //this.padWidth = Math.floor(padavail /  ( this.cols));
34953         
34954         // adjust colum width so that padding is fixed??
34955         
34956         // we have 3 columns ... total = width * 3
34957         // we have X left over... that should be used by 
34958         
34959         //if (this.expandC) {
34960             
34961         //}
34962         
34963         
34964         
34965     },
34966     
34967     getContainerWidth : function()
34968     {
34969        /* // container is parent if fit width
34970         var container = this.isFitWidth ? this.element.parentNode : this.element;
34971         // check that this.size and size are there
34972         // IE8 triggers resize on body size change, so they might not be
34973         
34974         var size = getSize( container );  //FIXME
34975         this.containerWidth = size && size.innerWidth; //FIXME
34976         */
34977          
34978         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34979         
34980     },
34981     
34982     _getItemLayoutPosition : function( item )  // what is item?
34983     {
34984         // we resize the item to our columnWidth..
34985       
34986         item.setWidth(this.columnWidth);
34987         item.autoBoxAdjust  = false;
34988         
34989         var sz = item.getSize();
34990  
34991         // how many columns does this brick span
34992         var remainder = this.containerWidth % this.columnWidth;
34993         
34994         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34995         // round if off by 1 pixel, otherwise use ceil
34996         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34997         colSpan = Math.min( colSpan, this.cols );
34998         
34999         // normally this should be '1' as we dont' currently allow multi width columns..
35000         
35001         var colGroup = this._getColGroup( colSpan );
35002         // get the minimum Y value from the columns
35003         var minimumY = Math.min.apply( Math, colGroup );
35004         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35005         
35006         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35007          
35008         // position the brick
35009         var position = {
35010             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35011             y: this.currentSize.y + minimumY + this.padHeight
35012         };
35013         
35014         Roo.log(position);
35015         // apply setHeight to necessary columns
35016         var setHeight = minimumY + sz.height + this.padHeight;
35017         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35018         
35019         var setSpan = this.cols + 1 - colGroup.length;
35020         for ( var i = 0; i < setSpan; i++ ) {
35021           this.colYs[ shortColIndex + i ] = setHeight ;
35022         }
35023       
35024         return position;
35025     },
35026     
35027     /**
35028      * @param {Number} colSpan - number of columns the element spans
35029      * @returns {Array} colGroup
35030      */
35031     _getColGroup : function( colSpan )
35032     {
35033         if ( colSpan < 2 ) {
35034           // if brick spans only one column, use all the column Ys
35035           return this.colYs;
35036         }
35037       
35038         var colGroup = [];
35039         // how many different places could this brick fit horizontally
35040         var groupCount = this.cols + 1 - colSpan;
35041         // for each group potential horizontal position
35042         for ( var i = 0; i < groupCount; i++ ) {
35043           // make an array of colY values for that one group
35044           var groupColYs = this.colYs.slice( i, i + colSpan );
35045           // and get the max value of the array
35046           colGroup[i] = Math.max.apply( Math, groupColYs );
35047         }
35048         return colGroup;
35049     },
35050     /*
35051     _manageStamp : function( stamp )
35052     {
35053         var stampSize =  stamp.getSize();
35054         var offset = stamp.getBox();
35055         // get the columns that this stamp affects
35056         var firstX = this.isOriginLeft ? offset.x : offset.right;
35057         var lastX = firstX + stampSize.width;
35058         var firstCol = Math.floor( firstX / this.columnWidth );
35059         firstCol = Math.max( 0, firstCol );
35060         
35061         var lastCol = Math.floor( lastX / this.columnWidth );
35062         // lastCol should not go over if multiple of columnWidth #425
35063         lastCol -= lastX % this.columnWidth ? 0 : 1;
35064         lastCol = Math.min( this.cols - 1, lastCol );
35065         
35066         // set colYs to bottom of the stamp
35067         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35068             stampSize.height;
35069             
35070         for ( var i = firstCol; i <= lastCol; i++ ) {
35071           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35072         }
35073     },
35074     */
35075     
35076     _getContainerSize : function()
35077     {
35078         this.maxY = Math.max.apply( Math, this.colYs );
35079         var size = {
35080             height: this.maxY
35081         };
35082       
35083         if ( this.isFitWidth ) {
35084             size.width = this._getContainerFitWidth();
35085         }
35086       
35087         return size;
35088     },
35089     
35090     _getContainerFitWidth : function()
35091     {
35092         var unusedCols = 0;
35093         // count unused columns
35094         var i = this.cols;
35095         while ( --i ) {
35096           if ( this.colYs[i] !== 0 ) {
35097             break;
35098           }
35099           unusedCols++;
35100         }
35101         // fit container to columns that have been used
35102         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35103     },
35104     
35105     needsResizeLayout : function()
35106     {
35107         var previousWidth = this.containerWidth;
35108         this.getContainerWidth();
35109         return previousWidth !== this.containerWidth;
35110     }
35111  
35112 });
35113
35114  
35115
35116  /*
35117  * - LGPL
35118  *
35119  * element
35120  * 
35121  */
35122
35123 /**
35124  * @class Roo.bootstrap.MasonryBrick
35125  * @extends Roo.bootstrap.Component
35126  * Bootstrap MasonryBrick class
35127  * 
35128  * @constructor
35129  * Create a new MasonryBrick
35130  * @param {Object} config The config object
35131  */
35132
35133 Roo.bootstrap.MasonryBrick = function(config){
35134     
35135     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35136     
35137     Roo.bootstrap.MasonryBrick.register(this);
35138     
35139     this.addEvents({
35140         // raw events
35141         /**
35142          * @event click
35143          * When a MasonryBrick is clcik
35144          * @param {Roo.bootstrap.MasonryBrick} this
35145          * @param {Roo.EventObject} e
35146          */
35147         "click" : true
35148     });
35149 };
35150
35151 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35152     
35153     /**
35154      * @cfg {String} title
35155      */   
35156     title : '',
35157     /**
35158      * @cfg {String} html
35159      */   
35160     html : '',
35161     /**
35162      * @cfg {String} bgimage
35163      */   
35164     bgimage : '',
35165     /**
35166      * @cfg {String} videourl
35167      */   
35168     videourl : '',
35169     /**
35170      * @cfg {String} cls
35171      */   
35172     cls : '',
35173     /**
35174      * @cfg {String} href
35175      */   
35176     href : '',
35177     /**
35178      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35179      */   
35180     size : 'xs',
35181     
35182     /**
35183      * @cfg {String} placetitle (center|bottom)
35184      */   
35185     placetitle : '',
35186     
35187     /**
35188      * @cfg {Boolean} isFitContainer defalut true
35189      */   
35190     isFitContainer : true, 
35191     
35192     /**
35193      * @cfg {Boolean} preventDefault defalut false
35194      */   
35195     preventDefault : false, 
35196     
35197     /**
35198      * @cfg {Boolean} inverse defalut false
35199      */   
35200     maskInverse : false, 
35201     
35202     getAutoCreate : function()
35203     {
35204         if(!this.isFitContainer){
35205             return this.getSplitAutoCreate();
35206         }
35207         
35208         var cls = 'masonry-brick masonry-brick-full';
35209         
35210         if(this.href.length){
35211             cls += ' masonry-brick-link';
35212         }
35213         
35214         if(this.bgimage.length){
35215             cls += ' masonry-brick-image';
35216         }
35217         
35218         if(this.maskInverse){
35219             cls += ' mask-inverse';
35220         }
35221         
35222         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35223             cls += ' enable-mask';
35224         }
35225         
35226         if(this.size){
35227             cls += ' masonry-' + this.size + '-brick';
35228         }
35229         
35230         if(this.placetitle.length){
35231             
35232             switch (this.placetitle) {
35233                 case 'center' :
35234                     cls += ' masonry-center-title';
35235                     break;
35236                 case 'bottom' :
35237                     cls += ' masonry-bottom-title';
35238                     break;
35239                 default:
35240                     break;
35241             }
35242             
35243         } else {
35244             if(!this.html.length && !this.bgimage.length){
35245                 cls += ' masonry-center-title';
35246             }
35247
35248             if(!this.html.length && this.bgimage.length){
35249                 cls += ' masonry-bottom-title';
35250             }
35251         }
35252         
35253         if(this.cls){
35254             cls += ' ' + this.cls;
35255         }
35256         
35257         var cfg = {
35258             tag: (this.href.length) ? 'a' : 'div',
35259             cls: cls,
35260             cn: [
35261                 {
35262                     tag: 'div',
35263                     cls: 'masonry-brick-mask'
35264                 },
35265                 {
35266                     tag: 'div',
35267                     cls: 'masonry-brick-paragraph',
35268                     cn: []
35269                 }
35270             ]
35271         };
35272         
35273         if(this.href.length){
35274             cfg.href = this.href;
35275         }
35276         
35277         var cn = cfg.cn[1].cn;
35278         
35279         if(this.title.length){
35280             cn.push({
35281                 tag: 'h4',
35282                 cls: 'masonry-brick-title',
35283                 html: this.title
35284             });
35285         }
35286         
35287         if(this.html.length){
35288             cn.push({
35289                 tag: 'p',
35290                 cls: 'masonry-brick-text',
35291                 html: this.html
35292             });
35293         }
35294         
35295         if (!this.title.length && !this.html.length) {
35296             cfg.cn[1].cls += ' hide';
35297         }
35298         
35299         if(this.bgimage.length){
35300             cfg.cn.push({
35301                 tag: 'img',
35302                 cls: 'masonry-brick-image-view',
35303                 src: this.bgimage
35304             });
35305         }
35306         
35307         if(this.videourl.length){
35308             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35309             // youtube support only?
35310             cfg.cn.push({
35311                 tag: 'iframe',
35312                 cls: 'masonry-brick-image-view',
35313                 src: vurl,
35314                 frameborder : 0,
35315                 allowfullscreen : true
35316             });
35317         }
35318         
35319         return cfg;
35320         
35321     },
35322     
35323     getSplitAutoCreate : function()
35324     {
35325         var cls = 'masonry-brick masonry-brick-split';
35326         
35327         if(this.href.length){
35328             cls += ' masonry-brick-link';
35329         }
35330         
35331         if(this.bgimage.length){
35332             cls += ' masonry-brick-image';
35333         }
35334         
35335         if(this.size){
35336             cls += ' masonry-' + this.size + '-brick';
35337         }
35338         
35339         switch (this.placetitle) {
35340             case 'center' :
35341                 cls += ' masonry-center-title';
35342                 break;
35343             case 'bottom' :
35344                 cls += ' masonry-bottom-title';
35345                 break;
35346             default:
35347                 if(!this.bgimage.length){
35348                     cls += ' masonry-center-title';
35349                 }
35350
35351                 if(this.bgimage.length){
35352                     cls += ' masonry-bottom-title';
35353                 }
35354                 break;
35355         }
35356         
35357         if(this.cls){
35358             cls += ' ' + this.cls;
35359         }
35360         
35361         var cfg = {
35362             tag: (this.href.length) ? 'a' : 'div',
35363             cls: cls,
35364             cn: [
35365                 {
35366                     tag: 'div',
35367                     cls: 'masonry-brick-split-head',
35368                     cn: [
35369                         {
35370                             tag: 'div',
35371                             cls: 'masonry-brick-paragraph',
35372                             cn: []
35373                         }
35374                     ]
35375                 },
35376                 {
35377                     tag: 'div',
35378                     cls: 'masonry-brick-split-body',
35379                     cn: []
35380                 }
35381             ]
35382         };
35383         
35384         if(this.href.length){
35385             cfg.href = this.href;
35386         }
35387         
35388         if(this.title.length){
35389             cfg.cn[0].cn[0].cn.push({
35390                 tag: 'h4',
35391                 cls: 'masonry-brick-title',
35392                 html: this.title
35393             });
35394         }
35395         
35396         if(this.html.length){
35397             cfg.cn[1].cn.push({
35398                 tag: 'p',
35399                 cls: 'masonry-brick-text',
35400                 html: this.html
35401             });
35402         }
35403
35404         if(this.bgimage.length){
35405             cfg.cn[0].cn.push({
35406                 tag: 'img',
35407                 cls: 'masonry-brick-image-view',
35408                 src: this.bgimage
35409             });
35410         }
35411         
35412         if(this.videourl.length){
35413             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35414             // youtube support only?
35415             cfg.cn[0].cn.cn.push({
35416                 tag: 'iframe',
35417                 cls: 'masonry-brick-image-view',
35418                 src: vurl,
35419                 frameborder : 0,
35420                 allowfullscreen : true
35421             });
35422         }
35423         
35424         return cfg;
35425     },
35426     
35427     initEvents: function() 
35428     {
35429         switch (this.size) {
35430             case 'xs' :
35431                 this.x = 1;
35432                 this.y = 1;
35433                 break;
35434             case 'sm' :
35435                 this.x = 2;
35436                 this.y = 2;
35437                 break;
35438             case 'md' :
35439             case 'md-left' :
35440             case 'md-right' :
35441                 this.x = 3;
35442                 this.y = 3;
35443                 break;
35444             case 'tall' :
35445                 this.x = 2;
35446                 this.y = 3;
35447                 break;
35448             case 'wide' :
35449                 this.x = 3;
35450                 this.y = 2;
35451                 break;
35452             case 'wide-thin' :
35453                 this.x = 3;
35454                 this.y = 1;
35455                 break;
35456                         
35457             default :
35458                 break;
35459         }
35460         
35461         if(Roo.isTouch){
35462             this.el.on('touchstart', this.onTouchStart, this);
35463             this.el.on('touchmove', this.onTouchMove, this);
35464             this.el.on('touchend', this.onTouchEnd, this);
35465             this.el.on('contextmenu', this.onContextMenu, this);
35466         } else {
35467             this.el.on('mouseenter'  ,this.enter, this);
35468             this.el.on('mouseleave', this.leave, this);
35469             this.el.on('click', this.onClick, this);
35470         }
35471         
35472         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35473             this.parent().bricks.push(this);   
35474         }
35475         
35476     },
35477     
35478     onClick: function(e, el)
35479     {
35480         var time = this.endTimer - this.startTimer;
35481         // Roo.log(e.preventDefault());
35482         if(Roo.isTouch){
35483             if(time > 1000){
35484                 e.preventDefault();
35485                 return;
35486             }
35487         }
35488         
35489         if(!this.preventDefault){
35490             return;
35491         }
35492         
35493         e.preventDefault();
35494         
35495         if (this.activeClass != '') {
35496             this.selectBrick();
35497         }
35498         
35499         this.fireEvent('click', this, e);
35500     },
35501     
35502     enter: function(e, el)
35503     {
35504         e.preventDefault();
35505         
35506         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35507             return;
35508         }
35509         
35510         if(this.bgimage.length && this.html.length){
35511             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35512         }
35513     },
35514     
35515     leave: function(e, el)
35516     {
35517         e.preventDefault();
35518         
35519         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35520             return;
35521         }
35522         
35523         if(this.bgimage.length && this.html.length){
35524             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35525         }
35526     },
35527     
35528     onTouchStart: function(e, el)
35529     {
35530 //        e.preventDefault();
35531         
35532         this.touchmoved = false;
35533         
35534         if(!this.isFitContainer){
35535             return;
35536         }
35537         
35538         if(!this.bgimage.length || !this.html.length){
35539             return;
35540         }
35541         
35542         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35543         
35544         this.timer = new Date().getTime();
35545         
35546     },
35547     
35548     onTouchMove: function(e, el)
35549     {
35550         this.touchmoved = true;
35551     },
35552     
35553     onContextMenu : function(e,el)
35554     {
35555         e.preventDefault();
35556         e.stopPropagation();
35557         return false;
35558     },
35559     
35560     onTouchEnd: function(e, el)
35561     {
35562 //        e.preventDefault();
35563         
35564         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35565         
35566             this.leave(e,el);
35567             
35568             return;
35569         }
35570         
35571         if(!this.bgimage.length || !this.html.length){
35572             
35573             if(this.href.length){
35574                 window.location.href = this.href;
35575             }
35576             
35577             return;
35578         }
35579         
35580         if(!this.isFitContainer){
35581             return;
35582         }
35583         
35584         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35585         
35586         window.location.href = this.href;
35587     },
35588     
35589     //selection on single brick only
35590     selectBrick : function() {
35591         
35592         if (!this.parentId) {
35593             return;
35594         }
35595         
35596         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35597         var index = m.selectedBrick.indexOf(this.id);
35598         
35599         if ( index > -1) {
35600             m.selectedBrick.splice(index,1);
35601             this.el.removeClass(this.activeClass);
35602             return;
35603         }
35604         
35605         for(var i = 0; i < m.selectedBrick.length; i++) {
35606             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35607             b.el.removeClass(b.activeClass);
35608         }
35609         
35610         m.selectedBrick = [];
35611         
35612         m.selectedBrick.push(this.id);
35613         this.el.addClass(this.activeClass);
35614         return;
35615     },
35616     
35617     isSelected : function(){
35618         return this.el.hasClass(this.activeClass);
35619         
35620     }
35621 });
35622
35623 Roo.apply(Roo.bootstrap.MasonryBrick, {
35624     
35625     //groups: {},
35626     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35627      /**
35628     * register a Masonry Brick
35629     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35630     */
35631     
35632     register : function(brick)
35633     {
35634         //this.groups[brick.id] = brick;
35635         this.groups.add(brick.id, brick);
35636     },
35637     /**
35638     * fetch a  masonry brick based on the masonry brick ID
35639     * @param {string} the masonry brick to add
35640     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35641     */
35642     
35643     get: function(brick_id) 
35644     {
35645         // if (typeof(this.groups[brick_id]) == 'undefined') {
35646         //     return false;
35647         // }
35648         // return this.groups[brick_id] ;
35649         
35650         if(this.groups.key(brick_id)) {
35651             return this.groups.key(brick_id);
35652         }
35653         
35654         return false;
35655     }
35656     
35657     
35658     
35659 });
35660
35661  /*
35662  * - LGPL
35663  *
35664  * element
35665  * 
35666  */
35667
35668 /**
35669  * @class Roo.bootstrap.Brick
35670  * @extends Roo.bootstrap.Component
35671  * Bootstrap Brick class
35672  * 
35673  * @constructor
35674  * Create a new Brick
35675  * @param {Object} config The config object
35676  */
35677
35678 Roo.bootstrap.Brick = function(config){
35679     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35680     
35681     this.addEvents({
35682         // raw events
35683         /**
35684          * @event click
35685          * When a Brick is click
35686          * @param {Roo.bootstrap.Brick} this
35687          * @param {Roo.EventObject} e
35688          */
35689         "click" : true
35690     });
35691 };
35692
35693 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35694     
35695     /**
35696      * @cfg {String} title
35697      */   
35698     title : '',
35699     /**
35700      * @cfg {String} html
35701      */   
35702     html : '',
35703     /**
35704      * @cfg {String} bgimage
35705      */   
35706     bgimage : '',
35707     /**
35708      * @cfg {String} cls
35709      */   
35710     cls : '',
35711     /**
35712      * @cfg {String} href
35713      */   
35714     href : '',
35715     /**
35716      * @cfg {String} video
35717      */   
35718     video : '',
35719     /**
35720      * @cfg {Boolean} square
35721      */   
35722     square : true,
35723     
35724     getAutoCreate : function()
35725     {
35726         var cls = 'roo-brick';
35727         
35728         if(this.href.length){
35729             cls += ' roo-brick-link';
35730         }
35731         
35732         if(this.bgimage.length){
35733             cls += ' roo-brick-image';
35734         }
35735         
35736         if(!this.html.length && !this.bgimage.length){
35737             cls += ' roo-brick-center-title';
35738         }
35739         
35740         if(!this.html.length && this.bgimage.length){
35741             cls += ' roo-brick-bottom-title';
35742         }
35743         
35744         if(this.cls){
35745             cls += ' ' + this.cls;
35746         }
35747         
35748         var cfg = {
35749             tag: (this.href.length) ? 'a' : 'div',
35750             cls: cls,
35751             cn: [
35752                 {
35753                     tag: 'div',
35754                     cls: 'roo-brick-paragraph',
35755                     cn: []
35756                 }
35757             ]
35758         };
35759         
35760         if(this.href.length){
35761             cfg.href = this.href;
35762         }
35763         
35764         var cn = cfg.cn[0].cn;
35765         
35766         if(this.title.length){
35767             cn.push({
35768                 tag: 'h4',
35769                 cls: 'roo-brick-title',
35770                 html: this.title
35771             });
35772         }
35773         
35774         if(this.html.length){
35775             cn.push({
35776                 tag: 'p',
35777                 cls: 'roo-brick-text',
35778                 html: this.html
35779             });
35780         } else {
35781             cn.cls += ' hide';
35782         }
35783         
35784         if(this.bgimage.length){
35785             cfg.cn.push({
35786                 tag: 'img',
35787                 cls: 'roo-brick-image-view',
35788                 src: this.bgimage
35789             });
35790         }
35791         
35792         return cfg;
35793     },
35794     
35795     initEvents: function() 
35796     {
35797         if(this.title.length || this.html.length){
35798             this.el.on('mouseenter'  ,this.enter, this);
35799             this.el.on('mouseleave', this.leave, this);
35800         }
35801         
35802         Roo.EventManager.onWindowResize(this.resize, this); 
35803         
35804         if(this.bgimage.length){
35805             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35806             this.imageEl.on('load', this.onImageLoad, this);
35807             return;
35808         }
35809         
35810         this.resize();
35811     },
35812     
35813     onImageLoad : function()
35814     {
35815         this.resize();
35816     },
35817     
35818     resize : function()
35819     {
35820         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35821         
35822         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35823         
35824         if(this.bgimage.length){
35825             var image = this.el.select('.roo-brick-image-view', true).first();
35826             
35827             image.setWidth(paragraph.getWidth());
35828             
35829             if(this.square){
35830                 image.setHeight(paragraph.getWidth());
35831             }
35832             
35833             this.el.setHeight(image.getHeight());
35834             paragraph.setHeight(image.getHeight());
35835             
35836         }
35837         
35838     },
35839     
35840     enter: function(e, el)
35841     {
35842         e.preventDefault();
35843         
35844         if(this.bgimage.length){
35845             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35846             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35847         }
35848     },
35849     
35850     leave: function(e, el)
35851     {
35852         e.preventDefault();
35853         
35854         if(this.bgimage.length){
35855             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35856             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35857         }
35858     }
35859     
35860 });
35861
35862  
35863
35864  /*
35865  * - LGPL
35866  *
35867  * Number field 
35868  */
35869
35870 /**
35871  * @class Roo.bootstrap.NumberField
35872  * @extends Roo.bootstrap.Input
35873  * Bootstrap NumberField class
35874  * 
35875  * 
35876  * 
35877  * 
35878  * @constructor
35879  * Create a new NumberField
35880  * @param {Object} config The config object
35881  */
35882
35883 Roo.bootstrap.NumberField = function(config){
35884     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35885 };
35886
35887 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35888     
35889     /**
35890      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35891      */
35892     allowDecimals : true,
35893     /**
35894      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35895      */
35896     decimalSeparator : ".",
35897     /**
35898      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35899      */
35900     decimalPrecision : 2,
35901     /**
35902      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35903      */
35904     allowNegative : true,
35905     
35906     /**
35907      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35908      */
35909     allowZero: true,
35910     /**
35911      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35912      */
35913     minValue : Number.NEGATIVE_INFINITY,
35914     /**
35915      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35916      */
35917     maxValue : Number.MAX_VALUE,
35918     /**
35919      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35920      */
35921     minText : "The minimum value for this field is {0}",
35922     /**
35923      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35924      */
35925     maxText : "The maximum value for this field is {0}",
35926     /**
35927      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35928      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35929      */
35930     nanText : "{0} is not a valid number",
35931     /**
35932      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35933      */
35934     thousandsDelimiter : false,
35935     /**
35936      * @cfg {String} valueAlign alignment of value
35937      */
35938     valueAlign : "left",
35939
35940     getAutoCreate : function()
35941     {
35942         var hiddenInput = {
35943             tag: 'input',
35944             type: 'hidden',
35945             id: Roo.id(),
35946             cls: 'hidden-number-input'
35947         };
35948         
35949         if (this.name) {
35950             hiddenInput.name = this.name;
35951         }
35952         
35953         this.name = '';
35954         
35955         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35956         
35957         this.name = hiddenInput.name;
35958         
35959         if(cfg.cn.length > 0) {
35960             cfg.cn.push(hiddenInput);
35961         }
35962         
35963         return cfg;
35964     },
35965
35966     // private
35967     initEvents : function()
35968     {   
35969         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35970         
35971         var allowed = "0123456789";
35972         
35973         if(this.allowDecimals){
35974             allowed += this.decimalSeparator;
35975         }
35976         
35977         if(this.allowNegative){
35978             allowed += "-";
35979         }
35980         
35981         if(this.thousandsDelimiter) {
35982             allowed += ",";
35983         }
35984         
35985         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35986         
35987         var keyPress = function(e){
35988             
35989             var k = e.getKey();
35990             
35991             var c = e.getCharCode();
35992             
35993             if(
35994                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35995                     allowed.indexOf(String.fromCharCode(c)) === -1
35996             ){
35997                 e.stopEvent();
35998                 return;
35999             }
36000             
36001             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36002                 return;
36003             }
36004             
36005             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36006                 e.stopEvent();
36007             }
36008         };
36009         
36010         this.el.on("keypress", keyPress, this);
36011     },
36012     
36013     validateValue : function(value)
36014     {
36015         
36016         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36017             return false;
36018         }
36019         
36020         var num = this.parseValue(value);
36021         
36022         if(isNaN(num)){
36023             this.markInvalid(String.format(this.nanText, value));
36024             return false;
36025         }
36026         
36027         if(num < this.minValue){
36028             this.markInvalid(String.format(this.minText, this.minValue));
36029             return false;
36030         }
36031         
36032         if(num > this.maxValue){
36033             this.markInvalid(String.format(this.maxText, this.maxValue));
36034             return false;
36035         }
36036         
36037         return true;
36038     },
36039
36040     getValue : function()
36041     {
36042         var v = this.hiddenEl().getValue();
36043         
36044         return this.fixPrecision(this.parseValue(v));
36045     },
36046
36047     parseValue : function(value)
36048     {
36049         if(this.thousandsDelimiter) {
36050             value += "";
36051             r = new RegExp(",", "g");
36052             value = value.replace(r, "");
36053         }
36054         
36055         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36056         return isNaN(value) ? '' : value;
36057     },
36058
36059     fixPrecision : function(value)
36060     {
36061         if(this.thousandsDelimiter) {
36062             value += "";
36063             r = new RegExp(",", "g");
36064             value = value.replace(r, "");
36065         }
36066         
36067         var nan = isNaN(value);
36068         
36069         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36070             return nan ? '' : value;
36071         }
36072         return parseFloat(value).toFixed(this.decimalPrecision);
36073     },
36074
36075     setValue : function(v)
36076     {
36077         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36078         
36079         this.value = v;
36080         
36081         if(this.rendered){
36082             
36083             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36084             
36085             this.inputEl().dom.value = (v == '') ? '' :
36086                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36087             
36088             if(!this.allowZero && v === '0') {
36089                 this.hiddenEl().dom.value = '';
36090                 this.inputEl().dom.value = '';
36091             }
36092             
36093             this.validate();
36094         }
36095     },
36096
36097     decimalPrecisionFcn : function(v)
36098     {
36099         return Math.floor(v);
36100     },
36101
36102     beforeBlur : function()
36103     {
36104         var v = this.parseValue(this.getRawValue());
36105         
36106         if(v || v === 0 || v === ''){
36107             this.setValue(v);
36108         }
36109     },
36110     
36111     hiddenEl : function()
36112     {
36113         return this.el.select('input.hidden-number-input',true).first();
36114     }
36115     
36116 });
36117
36118  
36119
36120 /*
36121 * Licence: LGPL
36122 */
36123
36124 /**
36125  * @class Roo.bootstrap.DocumentSlider
36126  * @extends Roo.bootstrap.Component
36127  * Bootstrap DocumentSlider class
36128  * 
36129  * @constructor
36130  * Create a new DocumentViewer
36131  * @param {Object} config The config object
36132  */
36133
36134 Roo.bootstrap.DocumentSlider = function(config){
36135     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36136     
36137     this.files = [];
36138     
36139     this.addEvents({
36140         /**
36141          * @event initial
36142          * Fire after initEvent
36143          * @param {Roo.bootstrap.DocumentSlider} this
36144          */
36145         "initial" : true,
36146         /**
36147          * @event update
36148          * Fire after update
36149          * @param {Roo.bootstrap.DocumentSlider} this
36150          */
36151         "update" : true,
36152         /**
36153          * @event click
36154          * Fire after click
36155          * @param {Roo.bootstrap.DocumentSlider} this
36156          */
36157         "click" : true
36158     });
36159 };
36160
36161 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36162     
36163     files : false,
36164     
36165     indicator : 0,
36166     
36167     getAutoCreate : function()
36168     {
36169         var cfg = {
36170             tag : 'div',
36171             cls : 'roo-document-slider',
36172             cn : [
36173                 {
36174                     tag : 'div',
36175                     cls : 'roo-document-slider-header',
36176                     cn : [
36177                         {
36178                             tag : 'div',
36179                             cls : 'roo-document-slider-header-title'
36180                         }
36181                     ]
36182                 },
36183                 {
36184                     tag : 'div',
36185                     cls : 'roo-document-slider-body',
36186                     cn : [
36187                         {
36188                             tag : 'div',
36189                             cls : 'roo-document-slider-prev',
36190                             cn : [
36191                                 {
36192                                     tag : 'i',
36193                                     cls : 'fa fa-chevron-left'
36194                                 }
36195                             ]
36196                         },
36197                         {
36198                             tag : 'div',
36199                             cls : 'roo-document-slider-thumb',
36200                             cn : [
36201                                 {
36202                                     tag : 'img',
36203                                     cls : 'roo-document-slider-image'
36204                                 }
36205                             ]
36206                         },
36207                         {
36208                             tag : 'div',
36209                             cls : 'roo-document-slider-next',
36210                             cn : [
36211                                 {
36212                                     tag : 'i',
36213                                     cls : 'fa fa-chevron-right'
36214                                 }
36215                             ]
36216                         }
36217                     ]
36218                 }
36219             ]
36220         };
36221         
36222         return cfg;
36223     },
36224     
36225     initEvents : function()
36226     {
36227         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36228         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36229         
36230         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36231         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36232         
36233         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36234         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36235         
36236         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36237         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36238         
36239         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36240         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36241         
36242         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36243         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36244         
36245         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36246         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36247         
36248         this.thumbEl.on('click', this.onClick, this);
36249         
36250         this.prevIndicator.on('click', this.prev, this);
36251         
36252         this.nextIndicator.on('click', this.next, this);
36253         
36254     },
36255     
36256     initial : function()
36257     {
36258         if(this.files.length){
36259             this.indicator = 1;
36260             this.update()
36261         }
36262         
36263         this.fireEvent('initial', this);
36264     },
36265     
36266     update : function()
36267     {
36268         this.imageEl.attr('src', this.files[this.indicator - 1]);
36269         
36270         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36271         
36272         this.prevIndicator.show();
36273         
36274         if(this.indicator == 1){
36275             this.prevIndicator.hide();
36276         }
36277         
36278         this.nextIndicator.show();
36279         
36280         if(this.indicator == this.files.length){
36281             this.nextIndicator.hide();
36282         }
36283         
36284         this.thumbEl.scrollTo('top');
36285         
36286         this.fireEvent('update', this);
36287     },
36288     
36289     onClick : function(e)
36290     {
36291         e.preventDefault();
36292         
36293         this.fireEvent('click', this);
36294     },
36295     
36296     prev : function(e)
36297     {
36298         e.preventDefault();
36299         
36300         this.indicator = Math.max(1, this.indicator - 1);
36301         
36302         this.update();
36303     },
36304     
36305     next : function(e)
36306     {
36307         e.preventDefault();
36308         
36309         this.indicator = Math.min(this.files.length, this.indicator + 1);
36310         
36311         this.update();
36312     }
36313 });
36314 /*
36315  * - LGPL
36316  *
36317  * RadioSet
36318  *
36319  *
36320  */
36321
36322 /**
36323  * @class Roo.bootstrap.RadioSet
36324  * @extends Roo.bootstrap.Input
36325  * Bootstrap RadioSet class
36326  * @cfg {String} indicatorpos (left|right) default left
36327  * @cfg {Boolean} inline (true|false) inline the element (default true)
36328  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36329  * @constructor
36330  * Create a new RadioSet
36331  * @param {Object} config The config object
36332  */
36333
36334 Roo.bootstrap.RadioSet = function(config){
36335     
36336     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36337     
36338     this.radioes = [];
36339     
36340     Roo.bootstrap.RadioSet.register(this);
36341     
36342     this.addEvents({
36343         /**
36344         * @event check
36345         * Fires when the element is checked or unchecked.
36346         * @param {Roo.bootstrap.RadioSet} this This radio
36347         * @param {Roo.bootstrap.Radio} item The checked item
36348         */
36349        check : true,
36350        /**
36351         * @event click
36352         * Fires when the element is click.
36353         * @param {Roo.bootstrap.RadioSet} this This radio set
36354         * @param {Roo.bootstrap.Radio} item The checked item
36355         * @param {Roo.EventObject} e The event object
36356         */
36357        click : true
36358     });
36359     
36360 };
36361
36362 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36363
36364     radioes : false,
36365     
36366     inline : true,
36367     
36368     weight : '',
36369     
36370     indicatorpos : 'left',
36371     
36372     getAutoCreate : function()
36373     {
36374         var label = {
36375             tag : 'label',
36376             cls : 'roo-radio-set-label',
36377             cn : [
36378                 {
36379                     tag : 'span',
36380                     html : this.fieldLabel
36381                 }
36382             ]
36383         };
36384         if (Roo.bootstrap.version == 3) {
36385             
36386             
36387             if(this.indicatorpos == 'left'){
36388                 label.cn.unshift({
36389                     tag : 'i',
36390                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36391                     tooltip : 'This field is required'
36392                 });
36393             } else {
36394                 label.cn.push({
36395                     tag : 'i',
36396                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36397                     tooltip : 'This field is required'
36398                 });
36399             }
36400         }
36401         var items = {
36402             tag : 'div',
36403             cls : 'roo-radio-set-items'
36404         };
36405         
36406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36407         
36408         if (align === 'left' && this.fieldLabel.length) {
36409             
36410             items = {
36411                 cls : "roo-radio-set-right", 
36412                 cn: [
36413                     items
36414                 ]
36415             };
36416             
36417             if(this.labelWidth > 12){
36418                 label.style = "width: " + this.labelWidth + 'px';
36419             }
36420             
36421             if(this.labelWidth < 13 && this.labelmd == 0){
36422                 this.labelmd = this.labelWidth;
36423             }
36424             
36425             if(this.labellg > 0){
36426                 label.cls += ' col-lg-' + this.labellg;
36427                 items.cls += ' col-lg-' + (12 - this.labellg);
36428             }
36429             
36430             if(this.labelmd > 0){
36431                 label.cls += ' col-md-' + this.labelmd;
36432                 items.cls += ' col-md-' + (12 - this.labelmd);
36433             }
36434             
36435             if(this.labelsm > 0){
36436                 label.cls += ' col-sm-' + this.labelsm;
36437                 items.cls += ' col-sm-' + (12 - this.labelsm);
36438             }
36439             
36440             if(this.labelxs > 0){
36441                 label.cls += ' col-xs-' + this.labelxs;
36442                 items.cls += ' col-xs-' + (12 - this.labelxs);
36443             }
36444         }
36445         
36446         var cfg = {
36447             tag : 'div',
36448             cls : 'roo-radio-set',
36449             cn : [
36450                 {
36451                     tag : 'input',
36452                     cls : 'roo-radio-set-input',
36453                     type : 'hidden',
36454                     name : this.name,
36455                     value : this.value ? this.value :  ''
36456                 },
36457                 label,
36458                 items
36459             ]
36460         };
36461         
36462         if(this.weight.length){
36463             cfg.cls += ' roo-radio-' + this.weight;
36464         }
36465         
36466         if(this.inline) {
36467             cfg.cls += ' roo-radio-set-inline';
36468         }
36469         
36470         var settings=this;
36471         ['xs','sm','md','lg'].map(function(size){
36472             if (settings[size]) {
36473                 cfg.cls += ' col-' + size + '-' + settings[size];
36474             }
36475         });
36476         
36477         return cfg;
36478         
36479     },
36480
36481     initEvents : function()
36482     {
36483         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36484         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36485         
36486         if(!this.fieldLabel.length){
36487             this.labelEl.hide();
36488         }
36489         
36490         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36491         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36492         
36493         this.indicator = this.indicatorEl();
36494         
36495         if(this.indicator){
36496             this.indicator.addClass('invisible');
36497         }
36498         
36499         this.originalValue = this.getValue();
36500         
36501     },
36502     
36503     inputEl: function ()
36504     {
36505         return this.el.select('.roo-radio-set-input', true).first();
36506     },
36507     
36508     getChildContainer : function()
36509     {
36510         return this.itemsEl;
36511     },
36512     
36513     register : function(item)
36514     {
36515         this.radioes.push(item);
36516         
36517     },
36518     
36519     validate : function()
36520     {   
36521         if(this.getVisibilityEl().hasClass('hidden')){
36522             return true;
36523         }
36524         
36525         var valid = false;
36526         
36527         Roo.each(this.radioes, function(i){
36528             if(!i.checked){
36529                 return;
36530             }
36531             
36532             valid = true;
36533             return false;
36534         });
36535         
36536         if(this.allowBlank) {
36537             return true;
36538         }
36539         
36540         if(this.disabled || valid){
36541             this.markValid();
36542             return true;
36543         }
36544         
36545         this.markInvalid();
36546         return false;
36547         
36548     },
36549     
36550     markValid : function()
36551     {
36552         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36553             this.indicatorEl().removeClass('visible');
36554             this.indicatorEl().addClass('invisible');
36555         }
36556         
36557         
36558         if (Roo.bootstrap.version == 3) {
36559             this.el.removeClass([this.invalidClass, this.validClass]);
36560             this.el.addClass(this.validClass);
36561         } else {
36562             this.el.removeClass(['is-invalid','is-valid']);
36563             this.el.addClass(['is-valid']);
36564         }
36565         this.fireEvent('valid', this);
36566     },
36567     
36568     markInvalid : function(msg)
36569     {
36570         if(this.allowBlank || this.disabled){
36571             return;
36572         }
36573         
36574         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36575             this.indicatorEl().removeClass('invisible');
36576             this.indicatorEl().addClass('visible');
36577         }
36578         if (Roo.bootstrap.version == 3) {
36579             this.el.removeClass([this.invalidClass, this.validClass]);
36580             this.el.addClass(this.invalidClass);
36581         } else {
36582             this.el.removeClass(['is-invalid','is-valid']);
36583             this.el.addClass(['is-invalid']);
36584         }
36585         
36586         this.fireEvent('invalid', this, msg);
36587         
36588     },
36589     
36590     setValue : function(v, suppressEvent)
36591     {   
36592         if(this.value === v){
36593             return;
36594         }
36595         
36596         this.value = v;
36597         
36598         if(this.rendered){
36599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36600         }
36601         
36602         Roo.each(this.radioes, function(i){
36603             i.checked = false;
36604             i.el.removeClass('checked');
36605         });
36606         
36607         Roo.each(this.radioes, function(i){
36608             
36609             if(i.value === v || i.value.toString() === v.toString()){
36610                 i.checked = true;
36611                 i.el.addClass('checked');
36612                 
36613                 if(suppressEvent !== true){
36614                     this.fireEvent('check', this, i);
36615                 }
36616                 
36617                 return false;
36618             }
36619             
36620         }, this);
36621         
36622         this.validate();
36623     },
36624     
36625     clearInvalid : function(){
36626         
36627         if(!this.el || this.preventMark){
36628             return;
36629         }
36630         
36631         this.el.removeClass([this.invalidClass]);
36632         
36633         this.fireEvent('valid', this);
36634     }
36635     
36636 });
36637
36638 Roo.apply(Roo.bootstrap.RadioSet, {
36639     
36640     groups: {},
36641     
36642     register : function(set)
36643     {
36644         this.groups[set.name] = set;
36645     },
36646     
36647     get: function(name) 
36648     {
36649         if (typeof(this.groups[name]) == 'undefined') {
36650             return false;
36651         }
36652         
36653         return this.groups[name] ;
36654     }
36655     
36656 });
36657 /*
36658  * Based on:
36659  * Ext JS Library 1.1.1
36660  * Copyright(c) 2006-2007, Ext JS, LLC.
36661  *
36662  * Originally Released Under LGPL - original licence link has changed is not relivant.
36663  *
36664  * Fork - LGPL
36665  * <script type="text/javascript">
36666  */
36667
36668
36669 /**
36670  * @class Roo.bootstrap.SplitBar
36671  * @extends Roo.util.Observable
36672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36673  * <br><br>
36674  * Usage:
36675  * <pre><code>
36676 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36677                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36678 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36679 split.minSize = 100;
36680 split.maxSize = 600;
36681 split.animate = true;
36682 split.on('moved', splitterMoved);
36683 </code></pre>
36684  * @constructor
36685  * Create a new SplitBar
36686  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36687  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36688  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36689  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36690                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36691                         position of the SplitBar).
36692  */
36693 Roo.bootstrap.SplitBar = function(cfg){
36694     
36695     /** @private */
36696     
36697     //{
36698     //  dragElement : elm
36699     //  resizingElement: el,
36700         // optional..
36701     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36702     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36703         // existingProxy ???
36704     //}
36705     
36706     this.el = Roo.get(cfg.dragElement, true);
36707     this.el.dom.unselectable = "on";
36708     /** @private */
36709     this.resizingEl = Roo.get(cfg.resizingElement, true);
36710
36711     /**
36712      * @private
36713      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36714      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36715      * @type Number
36716      */
36717     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36718     
36719     /**
36720      * The minimum size of the resizing element. (Defaults to 0)
36721      * @type Number
36722      */
36723     this.minSize = 0;
36724     
36725     /**
36726      * The maximum size of the resizing element. (Defaults to 2000)
36727      * @type Number
36728      */
36729     this.maxSize = 2000;
36730     
36731     /**
36732      * Whether to animate the transition to the new size
36733      * @type Boolean
36734      */
36735     this.animate = false;
36736     
36737     /**
36738      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36739      * @type Boolean
36740      */
36741     this.useShim = false;
36742     
36743     /** @private */
36744     this.shim = null;
36745     
36746     if(!cfg.existingProxy){
36747         /** @private */
36748         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36749     }else{
36750         this.proxy = Roo.get(cfg.existingProxy).dom;
36751     }
36752     /** @private */
36753     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36754     
36755     /** @private */
36756     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36757     
36758     /** @private */
36759     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36760     
36761     /** @private */
36762     this.dragSpecs = {};
36763     
36764     /**
36765      * @private The adapter to use to positon and resize elements
36766      */
36767     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36768     this.adapter.init(this);
36769     
36770     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36771         /** @private */
36772         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36773         this.el.addClass("roo-splitbar-h");
36774     }else{
36775         /** @private */
36776         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36777         this.el.addClass("roo-splitbar-v");
36778     }
36779     
36780     this.addEvents({
36781         /**
36782          * @event resize
36783          * Fires when the splitter is moved (alias for {@link #event-moved})
36784          * @param {Roo.bootstrap.SplitBar} this
36785          * @param {Number} newSize the new width or height
36786          */
36787         "resize" : true,
36788         /**
36789          * @event moved
36790          * Fires when the splitter is moved
36791          * @param {Roo.bootstrap.SplitBar} this
36792          * @param {Number} newSize the new width or height
36793          */
36794         "moved" : true,
36795         /**
36796          * @event beforeresize
36797          * Fires before the splitter is dragged
36798          * @param {Roo.bootstrap.SplitBar} this
36799          */
36800         "beforeresize" : true,
36801
36802         "beforeapply" : true
36803     });
36804
36805     Roo.util.Observable.call(this);
36806 };
36807
36808 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36809     onStartProxyDrag : function(x, y){
36810         this.fireEvent("beforeresize", this);
36811         if(!this.overlay){
36812             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36813             o.unselectable();
36814             o.enableDisplayMode("block");
36815             // all splitbars share the same overlay
36816             Roo.bootstrap.SplitBar.prototype.overlay = o;
36817         }
36818         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36819         this.overlay.show();
36820         Roo.get(this.proxy).setDisplayed("block");
36821         var size = this.adapter.getElementSize(this);
36822         this.activeMinSize = this.getMinimumSize();;
36823         this.activeMaxSize = this.getMaximumSize();;
36824         var c1 = size - this.activeMinSize;
36825         var c2 = Math.max(this.activeMaxSize - size, 0);
36826         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36827             this.dd.resetConstraints();
36828             this.dd.setXConstraint(
36829                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36830                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36831             );
36832             this.dd.setYConstraint(0, 0);
36833         }else{
36834             this.dd.resetConstraints();
36835             this.dd.setXConstraint(0, 0);
36836             this.dd.setYConstraint(
36837                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36838                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36839             );
36840          }
36841         this.dragSpecs.startSize = size;
36842         this.dragSpecs.startPoint = [x, y];
36843         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36844     },
36845     
36846     /** 
36847      * @private Called after the drag operation by the DDProxy
36848      */
36849     onEndProxyDrag : function(e){
36850         Roo.get(this.proxy).setDisplayed(false);
36851         var endPoint = Roo.lib.Event.getXY(e);
36852         if(this.overlay){
36853             this.overlay.hide();
36854         }
36855         var newSize;
36856         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36857             newSize = this.dragSpecs.startSize + 
36858                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36859                     endPoint[0] - this.dragSpecs.startPoint[0] :
36860                     this.dragSpecs.startPoint[0] - endPoint[0]
36861                 );
36862         }else{
36863             newSize = this.dragSpecs.startSize + 
36864                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36865                     endPoint[1] - this.dragSpecs.startPoint[1] :
36866                     this.dragSpecs.startPoint[1] - endPoint[1]
36867                 );
36868         }
36869         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36870         if(newSize != this.dragSpecs.startSize){
36871             if(this.fireEvent('beforeapply', this, newSize) !== false){
36872                 this.adapter.setElementSize(this, newSize);
36873                 this.fireEvent("moved", this, newSize);
36874                 this.fireEvent("resize", this, newSize);
36875             }
36876         }
36877     },
36878     
36879     /**
36880      * Get the adapter this SplitBar uses
36881      * @return The adapter object
36882      */
36883     getAdapter : function(){
36884         return this.adapter;
36885     },
36886     
36887     /**
36888      * Set the adapter this SplitBar uses
36889      * @param {Object} adapter A SplitBar adapter object
36890      */
36891     setAdapter : function(adapter){
36892         this.adapter = adapter;
36893         this.adapter.init(this);
36894     },
36895     
36896     /**
36897      * Gets the minimum size for the resizing element
36898      * @return {Number} The minimum size
36899      */
36900     getMinimumSize : function(){
36901         return this.minSize;
36902     },
36903     
36904     /**
36905      * Sets the minimum size for the resizing element
36906      * @param {Number} minSize The minimum size
36907      */
36908     setMinimumSize : function(minSize){
36909         this.minSize = minSize;
36910     },
36911     
36912     /**
36913      * Gets the maximum size for the resizing element
36914      * @return {Number} The maximum size
36915      */
36916     getMaximumSize : function(){
36917         return this.maxSize;
36918     },
36919     
36920     /**
36921      * Sets the maximum size for the resizing element
36922      * @param {Number} maxSize The maximum size
36923      */
36924     setMaximumSize : function(maxSize){
36925         this.maxSize = maxSize;
36926     },
36927     
36928     /**
36929      * Sets the initialize size for the resizing element
36930      * @param {Number} size The initial size
36931      */
36932     setCurrentSize : function(size){
36933         var oldAnimate = this.animate;
36934         this.animate = false;
36935         this.adapter.setElementSize(this, size);
36936         this.animate = oldAnimate;
36937     },
36938     
36939     /**
36940      * Destroy this splitbar. 
36941      * @param {Boolean} removeEl True to remove the element
36942      */
36943     destroy : function(removeEl){
36944         if(this.shim){
36945             this.shim.remove();
36946         }
36947         this.dd.unreg();
36948         this.proxy.parentNode.removeChild(this.proxy);
36949         if(removeEl){
36950             this.el.remove();
36951         }
36952     }
36953 });
36954
36955 /**
36956  * @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.
36957  */
36958 Roo.bootstrap.SplitBar.createProxy = function(dir){
36959     var proxy = new Roo.Element(document.createElement("div"));
36960     proxy.unselectable();
36961     var cls = 'roo-splitbar-proxy';
36962     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36963     document.body.appendChild(proxy.dom);
36964     return proxy.dom;
36965 };
36966
36967 /** 
36968  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36969  * Default Adapter. It assumes the splitter and resizing element are not positioned
36970  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36971  */
36972 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36973 };
36974
36975 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36976     // do nothing for now
36977     init : function(s){
36978     
36979     },
36980     /**
36981      * Called before drag operations to get the current size of the resizing element. 
36982      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36983      */
36984      getElementSize : function(s){
36985         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36986             return s.resizingEl.getWidth();
36987         }else{
36988             return s.resizingEl.getHeight();
36989         }
36990     },
36991     
36992     /**
36993      * Called after drag operations to set the size of the resizing element.
36994      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36995      * @param {Number} newSize The new size to set
36996      * @param {Function} onComplete A function to be invoked when resizing is complete
36997      */
36998     setElementSize : function(s, newSize, onComplete){
36999         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37000             if(!s.animate){
37001                 s.resizingEl.setWidth(newSize);
37002                 if(onComplete){
37003                     onComplete(s, newSize);
37004                 }
37005             }else{
37006                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37007             }
37008         }else{
37009             
37010             if(!s.animate){
37011                 s.resizingEl.setHeight(newSize);
37012                 if(onComplete){
37013                     onComplete(s, newSize);
37014                 }
37015             }else{
37016                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37017             }
37018         }
37019     }
37020 };
37021
37022 /** 
37023  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37024  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37025  * Adapter that  moves the splitter element to align with the resized sizing element. 
37026  * Used with an absolute positioned SplitBar.
37027  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37028  * document.body, make sure you assign an id to the body element.
37029  */
37030 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37031     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37032     this.container = Roo.get(container);
37033 };
37034
37035 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37036     init : function(s){
37037         this.basic.init(s);
37038     },
37039     
37040     getElementSize : function(s){
37041         return this.basic.getElementSize(s);
37042     },
37043     
37044     setElementSize : function(s, newSize, onComplete){
37045         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37046     },
37047     
37048     moveSplitter : function(s){
37049         var yes = Roo.bootstrap.SplitBar;
37050         switch(s.placement){
37051             case yes.LEFT:
37052                 s.el.setX(s.resizingEl.getRight());
37053                 break;
37054             case yes.RIGHT:
37055                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37056                 break;
37057             case yes.TOP:
37058                 s.el.setY(s.resizingEl.getBottom());
37059                 break;
37060             case yes.BOTTOM:
37061                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37062                 break;
37063         }
37064     }
37065 };
37066
37067 /**
37068  * Orientation constant - Create a vertical SplitBar
37069  * @static
37070  * @type Number
37071  */
37072 Roo.bootstrap.SplitBar.VERTICAL = 1;
37073
37074 /**
37075  * Orientation constant - Create a horizontal SplitBar
37076  * @static
37077  * @type Number
37078  */
37079 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37080
37081 /**
37082  * Placement constant - The resizing element is to the left of the splitter element
37083  * @static
37084  * @type Number
37085  */
37086 Roo.bootstrap.SplitBar.LEFT = 1;
37087
37088 /**
37089  * Placement constant - The resizing element is to the right of the splitter element
37090  * @static
37091  * @type Number
37092  */
37093 Roo.bootstrap.SplitBar.RIGHT = 2;
37094
37095 /**
37096  * Placement constant - The resizing element is positioned above the splitter element
37097  * @static
37098  * @type Number
37099  */
37100 Roo.bootstrap.SplitBar.TOP = 3;
37101
37102 /**
37103  * Placement constant - The resizing element is positioned under splitter element
37104  * @static
37105  * @type Number
37106  */
37107 Roo.bootstrap.SplitBar.BOTTOM = 4;
37108 Roo.namespace("Roo.bootstrap.layout");/*
37109  * Based on:
37110  * Ext JS Library 1.1.1
37111  * Copyright(c) 2006-2007, Ext JS, LLC.
37112  *
37113  * Originally Released Under LGPL - original licence link has changed is not relivant.
37114  *
37115  * Fork - LGPL
37116  * <script type="text/javascript">
37117  */
37118
37119 /**
37120  * @class Roo.bootstrap.layout.Manager
37121  * @extends Roo.bootstrap.Component
37122  * Base class for layout managers.
37123  */
37124 Roo.bootstrap.layout.Manager = function(config)
37125 {
37126     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37127
37128
37129
37130
37131
37132     /** false to disable window resize monitoring @type Boolean */
37133     this.monitorWindowResize = true;
37134     this.regions = {};
37135     this.addEvents({
37136         /**
37137          * @event layout
37138          * Fires when a layout is performed.
37139          * @param {Roo.LayoutManager} this
37140          */
37141         "layout" : true,
37142         /**
37143          * @event regionresized
37144          * Fires when the user resizes a region.
37145          * @param {Roo.LayoutRegion} region The resized region
37146          * @param {Number} newSize The new size (width for east/west, height for north/south)
37147          */
37148         "regionresized" : true,
37149         /**
37150          * @event regioncollapsed
37151          * Fires when a region is collapsed.
37152          * @param {Roo.LayoutRegion} region The collapsed region
37153          */
37154         "regioncollapsed" : true,
37155         /**
37156          * @event regionexpanded
37157          * Fires when a region is expanded.
37158          * @param {Roo.LayoutRegion} region The expanded region
37159          */
37160         "regionexpanded" : true
37161     });
37162     this.updating = false;
37163
37164     if (config.el) {
37165         this.el = Roo.get(config.el);
37166         this.initEvents();
37167     }
37168
37169 };
37170
37171 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37172
37173
37174     regions : null,
37175
37176     monitorWindowResize : true,
37177
37178
37179     updating : false,
37180
37181
37182     onRender : function(ct, position)
37183     {
37184         if(!this.el){
37185             this.el = Roo.get(ct);
37186             this.initEvents();
37187         }
37188         //this.fireEvent('render',this);
37189     },
37190
37191
37192     initEvents: function()
37193     {
37194
37195
37196         // ie scrollbar fix
37197         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37198             document.body.scroll = "no";
37199         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37200             this.el.position('relative');
37201         }
37202         this.id = this.el.id;
37203         this.el.addClass("roo-layout-container");
37204         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37205         if(this.el.dom != document.body ) {
37206             this.el.on('resize', this.layout,this);
37207             this.el.on('show', this.layout,this);
37208         }
37209
37210     },
37211
37212     /**
37213      * Returns true if this layout is currently being updated
37214      * @return {Boolean}
37215      */
37216     isUpdating : function(){
37217         return this.updating;
37218     },
37219
37220     /**
37221      * Suspend the LayoutManager from doing auto-layouts while
37222      * making multiple add or remove calls
37223      */
37224     beginUpdate : function(){
37225         this.updating = true;
37226     },
37227
37228     /**
37229      * Restore auto-layouts and optionally disable the manager from performing a layout
37230      * @param {Boolean} noLayout true to disable a layout update
37231      */
37232     endUpdate : function(noLayout){
37233         this.updating = false;
37234         if(!noLayout){
37235             this.layout();
37236         }
37237     },
37238
37239     layout: function(){
37240         // abstract...
37241     },
37242
37243     onRegionResized : function(region, newSize){
37244         this.fireEvent("regionresized", region, newSize);
37245         this.layout();
37246     },
37247
37248     onRegionCollapsed : function(region){
37249         this.fireEvent("regioncollapsed", region);
37250     },
37251
37252     onRegionExpanded : function(region){
37253         this.fireEvent("regionexpanded", region);
37254     },
37255
37256     /**
37257      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37258      * performs box-model adjustments.
37259      * @return {Object} The size as an object {width: (the width), height: (the height)}
37260      */
37261     getViewSize : function()
37262     {
37263         var size;
37264         if(this.el.dom != document.body){
37265             size = this.el.getSize();
37266         }else{
37267             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37268         }
37269         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37270         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37271         return size;
37272     },
37273
37274     /**
37275      * Returns the Element this layout is bound to.
37276      * @return {Roo.Element}
37277      */
37278     getEl : function(){
37279         return this.el;
37280     },
37281
37282     /**
37283      * Returns the specified region.
37284      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37285      * @return {Roo.LayoutRegion}
37286      */
37287     getRegion : function(target){
37288         return this.regions[target.toLowerCase()];
37289     },
37290
37291     onWindowResize : function(){
37292         if(this.monitorWindowResize){
37293             this.layout();
37294         }
37295     }
37296 });
37297 /*
37298  * Based on:
37299  * Ext JS Library 1.1.1
37300  * Copyright(c) 2006-2007, Ext JS, LLC.
37301  *
37302  * Originally Released Under LGPL - original licence link has changed is not relivant.
37303  *
37304  * Fork - LGPL
37305  * <script type="text/javascript">
37306  */
37307 /**
37308  * @class Roo.bootstrap.layout.Border
37309  * @extends Roo.bootstrap.layout.Manager
37310  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37311  * please see: examples/bootstrap/nested.html<br><br>
37312  
37313 <b>The container the layout is rendered into can be either the body element or any other element.
37314 If it is not the body element, the container needs to either be an absolute positioned element,
37315 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37316 the container size if it is not the body element.</b>
37317
37318 * @constructor
37319 * Create a new Border
37320 * @param {Object} config Configuration options
37321  */
37322 Roo.bootstrap.layout.Border = function(config){
37323     config = config || {};
37324     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37325     
37326     
37327     
37328     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37329         if(config[region]){
37330             config[region].region = region;
37331             this.addRegion(config[region]);
37332         }
37333     },this);
37334     
37335 };
37336
37337 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37338
37339 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37340     
37341     parent : false, // this might point to a 'nest' or a ???
37342     
37343     /**
37344      * Creates and adds a new region if it doesn't already exist.
37345      * @param {String} target The target region key (north, south, east, west or center).
37346      * @param {Object} config The regions config object
37347      * @return {BorderLayoutRegion} The new region
37348      */
37349     addRegion : function(config)
37350     {
37351         if(!this.regions[config.region]){
37352             var r = this.factory(config);
37353             this.bindRegion(r);
37354         }
37355         return this.regions[config.region];
37356     },
37357
37358     // private (kinda)
37359     bindRegion : function(r){
37360         this.regions[r.config.region] = r;
37361         
37362         r.on("visibilitychange",    this.layout, this);
37363         r.on("paneladded",          this.layout, this);
37364         r.on("panelremoved",        this.layout, this);
37365         r.on("invalidated",         this.layout, this);
37366         r.on("resized",             this.onRegionResized, this);
37367         r.on("collapsed",           this.onRegionCollapsed, this);
37368         r.on("expanded",            this.onRegionExpanded, this);
37369     },
37370
37371     /**
37372      * Performs a layout update.
37373      */
37374     layout : function()
37375     {
37376         if(this.updating) {
37377             return;
37378         }
37379         
37380         // render all the rebions if they have not been done alreayd?
37381         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37382             if(this.regions[region] && !this.regions[region].bodyEl){
37383                 this.regions[region].onRender(this.el)
37384             }
37385         },this);
37386         
37387         var size = this.getViewSize();
37388         var w = size.width;
37389         var h = size.height;
37390         var centerW = w;
37391         var centerH = h;
37392         var centerY = 0;
37393         var centerX = 0;
37394         //var x = 0, y = 0;
37395
37396         var rs = this.regions;
37397         var north = rs["north"];
37398         var south = rs["south"]; 
37399         var west = rs["west"];
37400         var east = rs["east"];
37401         var center = rs["center"];
37402         //if(this.hideOnLayout){ // not supported anymore
37403             //c.el.setStyle("display", "none");
37404         //}
37405         if(north && north.isVisible()){
37406             var b = north.getBox();
37407             var m = north.getMargins();
37408             b.width = w - (m.left+m.right);
37409             b.x = m.left;
37410             b.y = m.top;
37411             centerY = b.height + b.y + m.bottom;
37412             centerH -= centerY;
37413             north.updateBox(this.safeBox(b));
37414         }
37415         if(south && south.isVisible()){
37416             var b = south.getBox();
37417             var m = south.getMargins();
37418             b.width = w - (m.left+m.right);
37419             b.x = m.left;
37420             var totalHeight = (b.height + m.top + m.bottom);
37421             b.y = h - totalHeight + m.top;
37422             centerH -= totalHeight;
37423             south.updateBox(this.safeBox(b));
37424         }
37425         if(west && west.isVisible()){
37426             var b = west.getBox();
37427             var m = west.getMargins();
37428             b.height = centerH - (m.top+m.bottom);
37429             b.x = m.left;
37430             b.y = centerY + m.top;
37431             var totalWidth = (b.width + m.left + m.right);
37432             centerX += totalWidth;
37433             centerW -= totalWidth;
37434             west.updateBox(this.safeBox(b));
37435         }
37436         if(east && east.isVisible()){
37437             var b = east.getBox();
37438             var m = east.getMargins();
37439             b.height = centerH - (m.top+m.bottom);
37440             var totalWidth = (b.width + m.left + m.right);
37441             b.x = w - totalWidth + m.left;
37442             b.y = centerY + m.top;
37443             centerW -= totalWidth;
37444             east.updateBox(this.safeBox(b));
37445         }
37446         if(center){
37447             var m = center.getMargins();
37448             var centerBox = {
37449                 x: centerX + m.left,
37450                 y: centerY + m.top,
37451                 width: centerW - (m.left+m.right),
37452                 height: centerH - (m.top+m.bottom)
37453             };
37454             //if(this.hideOnLayout){
37455                 //center.el.setStyle("display", "block");
37456             //}
37457             center.updateBox(this.safeBox(centerBox));
37458         }
37459         this.el.repaint();
37460         this.fireEvent("layout", this);
37461     },
37462
37463     // private
37464     safeBox : function(box){
37465         box.width = Math.max(0, box.width);
37466         box.height = Math.max(0, box.height);
37467         return box;
37468     },
37469
37470     /**
37471      * Adds a ContentPanel (or subclass) to this layout.
37472      * @param {String} target The target region key (north, south, east, west or center).
37473      * @param {Roo.ContentPanel} panel The panel to add
37474      * @return {Roo.ContentPanel} The added panel
37475      */
37476     add : function(target, panel){
37477          
37478         target = target.toLowerCase();
37479         return this.regions[target].add(panel);
37480     },
37481
37482     /**
37483      * Remove a ContentPanel (or subclass) to this layout.
37484      * @param {String} target The target region key (north, south, east, west or center).
37485      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37486      * @return {Roo.ContentPanel} The removed panel
37487      */
37488     remove : function(target, panel){
37489         target = target.toLowerCase();
37490         return this.regions[target].remove(panel);
37491     },
37492
37493     /**
37494      * Searches all regions for a panel with the specified id
37495      * @param {String} panelId
37496      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37497      */
37498     findPanel : function(panelId){
37499         var rs = this.regions;
37500         for(var target in rs){
37501             if(typeof rs[target] != "function"){
37502                 var p = rs[target].getPanel(panelId);
37503                 if(p){
37504                     return p;
37505                 }
37506             }
37507         }
37508         return null;
37509     },
37510
37511     /**
37512      * Searches all regions for a panel with the specified id and activates (shows) it.
37513      * @param {String/ContentPanel} panelId The panels id or the panel itself
37514      * @return {Roo.ContentPanel} The shown panel or null
37515      */
37516     showPanel : function(panelId) {
37517       var rs = this.regions;
37518       for(var target in rs){
37519          var r = rs[target];
37520          if(typeof r != "function"){
37521             if(r.hasPanel(panelId)){
37522                return r.showPanel(panelId);
37523             }
37524          }
37525       }
37526       return null;
37527    },
37528
37529    /**
37530      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37531      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37532      */
37533    /*
37534     restoreState : function(provider){
37535         if(!provider){
37536             provider = Roo.state.Manager;
37537         }
37538         var sm = new Roo.LayoutStateManager();
37539         sm.init(this, provider);
37540     },
37541 */
37542  
37543  
37544     /**
37545      * Adds a xtype elements to the layout.
37546      * <pre><code>
37547
37548 layout.addxtype({
37549        xtype : 'ContentPanel',
37550        region: 'west',
37551        items: [ .... ]
37552    }
37553 );
37554
37555 layout.addxtype({
37556         xtype : 'NestedLayoutPanel',
37557         region: 'west',
37558         layout: {
37559            center: { },
37560            west: { }   
37561         },
37562         items : [ ... list of content panels or nested layout panels.. ]
37563    }
37564 );
37565 </code></pre>
37566      * @param {Object} cfg Xtype definition of item to add.
37567      */
37568     addxtype : function(cfg)
37569     {
37570         // basically accepts a pannel...
37571         // can accept a layout region..!?!?
37572         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37573         
37574         
37575         // theory?  children can only be panels??
37576         
37577         //if (!cfg.xtype.match(/Panel$/)) {
37578         //    return false;
37579         //}
37580         var ret = false;
37581         
37582         if (typeof(cfg.region) == 'undefined') {
37583             Roo.log("Failed to add Panel, region was not set");
37584             Roo.log(cfg);
37585             return false;
37586         }
37587         var region = cfg.region;
37588         delete cfg.region;
37589         
37590           
37591         var xitems = [];
37592         if (cfg.items) {
37593             xitems = cfg.items;
37594             delete cfg.items;
37595         }
37596         var nb = false;
37597         
37598         if ( region == 'center') {
37599             Roo.log("Center: " + cfg.title);
37600         }
37601         
37602         
37603         switch(cfg.xtype) 
37604         {
37605             case 'Content':  // ContentPanel (el, cfg)
37606             case 'Scroll':  // ContentPanel (el, cfg)
37607             case 'View': 
37608                 cfg.autoCreate = cfg.autoCreate || true;
37609                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37610                 //} else {
37611                 //    var el = this.el.createChild();
37612                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37613                 //}
37614                 
37615                 this.add(region, ret);
37616                 break;
37617             
37618             /*
37619             case 'TreePanel': // our new panel!
37620                 cfg.el = this.el.createChild();
37621                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37622                 this.add(region, ret);
37623                 break;
37624             */
37625             
37626             case 'Nest': 
37627                 // create a new Layout (which is  a Border Layout...
37628                 
37629                 var clayout = cfg.layout;
37630                 clayout.el  = this.el.createChild();
37631                 clayout.items   = clayout.items  || [];
37632                 
37633                 delete cfg.layout;
37634                 
37635                 // replace this exitems with the clayout ones..
37636                 xitems = clayout.items;
37637                  
37638                 // force background off if it's in center...
37639                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37640                     cfg.background = false;
37641                 }
37642                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37643                 
37644                 
37645                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37646                 //console.log('adding nested layout panel '  + cfg.toSource());
37647                 this.add(region, ret);
37648                 nb = {}; /// find first...
37649                 break;
37650             
37651             case 'Grid':
37652                 
37653                 // needs grid and region
37654                 
37655                 //var el = this.getRegion(region).el.createChild();
37656                 /*
37657                  *var el = this.el.createChild();
37658                 // create the grid first...
37659                 cfg.grid.container = el;
37660                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37661                 */
37662                 
37663                 if (region == 'center' && this.active ) {
37664                     cfg.background = false;
37665                 }
37666                 
37667                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37668                 
37669                 this.add(region, ret);
37670                 /*
37671                 if (cfg.background) {
37672                     // render grid on panel activation (if panel background)
37673                     ret.on('activate', function(gp) {
37674                         if (!gp.grid.rendered) {
37675                     //        gp.grid.render(el);
37676                         }
37677                     });
37678                 } else {
37679                   //  cfg.grid.render(el);
37680                 }
37681                 */
37682                 break;
37683            
37684            
37685             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37686                 // it was the old xcomponent building that caused this before.
37687                 // espeically if border is the top element in the tree.
37688                 ret = this;
37689                 break; 
37690                 
37691                     
37692                 
37693                 
37694                 
37695             default:
37696                 /*
37697                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37698                     
37699                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37700                     this.add(region, ret);
37701                 } else {
37702                 */
37703                     Roo.log(cfg);
37704                     throw "Can not add '" + cfg.xtype + "' to Border";
37705                     return null;
37706              
37707                                 
37708              
37709         }
37710         this.beginUpdate();
37711         // add children..
37712         var region = '';
37713         var abn = {};
37714         Roo.each(xitems, function(i)  {
37715             region = nb && i.region ? i.region : false;
37716             
37717             var add = ret.addxtype(i);
37718            
37719             if (region) {
37720                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37721                 if (!i.background) {
37722                     abn[region] = nb[region] ;
37723                 }
37724             }
37725             
37726         });
37727         this.endUpdate();
37728
37729         // make the last non-background panel active..
37730         //if (nb) { Roo.log(abn); }
37731         if (nb) {
37732             
37733             for(var r in abn) {
37734                 region = this.getRegion(r);
37735                 if (region) {
37736                     // tried using nb[r], but it does not work..
37737                      
37738                     region.showPanel(abn[r]);
37739                    
37740                 }
37741             }
37742         }
37743         return ret;
37744         
37745     },
37746     
37747     
37748 // private
37749     factory : function(cfg)
37750     {
37751         
37752         var validRegions = Roo.bootstrap.layout.Border.regions;
37753
37754         var target = cfg.region;
37755         cfg.mgr = this;
37756         
37757         var r = Roo.bootstrap.layout;
37758         Roo.log(target);
37759         switch(target){
37760             case "north":
37761                 return new r.North(cfg);
37762             case "south":
37763                 return new r.South(cfg);
37764             case "east":
37765                 return new r.East(cfg);
37766             case "west":
37767                 return new r.West(cfg);
37768             case "center":
37769                 return new r.Center(cfg);
37770         }
37771         throw 'Layout region "'+target+'" not supported.';
37772     }
37773     
37774     
37775 });
37776  /*
37777  * Based on:
37778  * Ext JS Library 1.1.1
37779  * Copyright(c) 2006-2007, Ext JS, LLC.
37780  *
37781  * Originally Released Under LGPL - original licence link has changed is not relivant.
37782  *
37783  * Fork - LGPL
37784  * <script type="text/javascript">
37785  */
37786  
37787 /**
37788  * @class Roo.bootstrap.layout.Basic
37789  * @extends Roo.util.Observable
37790  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37791  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37792  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37793  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37794  * @cfg {string}   region  the region that it inhabits..
37795  * @cfg {bool}   skipConfig skip config?
37796  * 
37797
37798  */
37799 Roo.bootstrap.layout.Basic = function(config){
37800     
37801     this.mgr = config.mgr;
37802     
37803     this.position = config.region;
37804     
37805     var skipConfig = config.skipConfig;
37806     
37807     this.events = {
37808         /**
37809          * @scope Roo.BasicLayoutRegion
37810          */
37811         
37812         /**
37813          * @event beforeremove
37814          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37815          * @param {Roo.LayoutRegion} this
37816          * @param {Roo.ContentPanel} panel The panel
37817          * @param {Object} e The cancel event object
37818          */
37819         "beforeremove" : true,
37820         /**
37821          * @event invalidated
37822          * Fires when the layout for this region is changed.
37823          * @param {Roo.LayoutRegion} this
37824          */
37825         "invalidated" : true,
37826         /**
37827          * @event visibilitychange
37828          * Fires when this region is shown or hidden 
37829          * @param {Roo.LayoutRegion} this
37830          * @param {Boolean} visibility true or false
37831          */
37832         "visibilitychange" : true,
37833         /**
37834          * @event paneladded
37835          * Fires when a panel is added. 
37836          * @param {Roo.LayoutRegion} this
37837          * @param {Roo.ContentPanel} panel The panel
37838          */
37839         "paneladded" : true,
37840         /**
37841          * @event panelremoved
37842          * Fires when a panel is removed. 
37843          * @param {Roo.LayoutRegion} this
37844          * @param {Roo.ContentPanel} panel The panel
37845          */
37846         "panelremoved" : true,
37847         /**
37848          * @event beforecollapse
37849          * Fires when this region before collapse.
37850          * @param {Roo.LayoutRegion} this
37851          */
37852         "beforecollapse" : true,
37853         /**
37854          * @event collapsed
37855          * Fires when this region is collapsed.
37856          * @param {Roo.LayoutRegion} this
37857          */
37858         "collapsed" : true,
37859         /**
37860          * @event expanded
37861          * Fires when this region is expanded.
37862          * @param {Roo.LayoutRegion} this
37863          */
37864         "expanded" : true,
37865         /**
37866          * @event slideshow
37867          * Fires when this region is slid into view.
37868          * @param {Roo.LayoutRegion} this
37869          */
37870         "slideshow" : true,
37871         /**
37872          * @event slidehide
37873          * Fires when this region slides out of view. 
37874          * @param {Roo.LayoutRegion} this
37875          */
37876         "slidehide" : true,
37877         /**
37878          * @event panelactivated
37879          * Fires when a panel is activated. 
37880          * @param {Roo.LayoutRegion} this
37881          * @param {Roo.ContentPanel} panel The activated panel
37882          */
37883         "panelactivated" : true,
37884         /**
37885          * @event resized
37886          * Fires when the user resizes this region. 
37887          * @param {Roo.LayoutRegion} this
37888          * @param {Number} newSize The new size (width for east/west, height for north/south)
37889          */
37890         "resized" : true
37891     };
37892     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37893     this.panels = new Roo.util.MixedCollection();
37894     this.panels.getKey = this.getPanelId.createDelegate(this);
37895     this.box = null;
37896     this.activePanel = null;
37897     // ensure listeners are added...
37898     
37899     if (config.listeners || config.events) {
37900         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37901             listeners : config.listeners || {},
37902             events : config.events || {}
37903         });
37904     }
37905     
37906     if(skipConfig !== true){
37907         this.applyConfig(config);
37908     }
37909 };
37910
37911 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37912 {
37913     getPanelId : function(p){
37914         return p.getId();
37915     },
37916     
37917     applyConfig : function(config){
37918         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37919         this.config = config;
37920         
37921     },
37922     
37923     /**
37924      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37925      * the width, for horizontal (north, south) the height.
37926      * @param {Number} newSize The new width or height
37927      */
37928     resizeTo : function(newSize){
37929         var el = this.el ? this.el :
37930                  (this.activePanel ? this.activePanel.getEl() : null);
37931         if(el){
37932             switch(this.position){
37933                 case "east":
37934                 case "west":
37935                     el.setWidth(newSize);
37936                     this.fireEvent("resized", this, newSize);
37937                 break;
37938                 case "north":
37939                 case "south":
37940                     el.setHeight(newSize);
37941                     this.fireEvent("resized", this, newSize);
37942                 break;                
37943             }
37944         }
37945     },
37946     
37947     getBox : function(){
37948         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37949     },
37950     
37951     getMargins : function(){
37952         return this.margins;
37953     },
37954     
37955     updateBox : function(box){
37956         this.box = box;
37957         var el = this.activePanel.getEl();
37958         el.dom.style.left = box.x + "px";
37959         el.dom.style.top = box.y + "px";
37960         this.activePanel.setSize(box.width, box.height);
37961     },
37962     
37963     /**
37964      * Returns the container element for this region.
37965      * @return {Roo.Element}
37966      */
37967     getEl : function(){
37968         return this.activePanel;
37969     },
37970     
37971     /**
37972      * Returns true if this region is currently visible.
37973      * @return {Boolean}
37974      */
37975     isVisible : function(){
37976         return this.activePanel ? true : false;
37977     },
37978     
37979     setActivePanel : function(panel){
37980         panel = this.getPanel(panel);
37981         if(this.activePanel && this.activePanel != panel){
37982             this.activePanel.setActiveState(false);
37983             this.activePanel.getEl().setLeftTop(-10000,-10000);
37984         }
37985         this.activePanel = panel;
37986         panel.setActiveState(true);
37987         if(this.box){
37988             panel.setSize(this.box.width, this.box.height);
37989         }
37990         this.fireEvent("panelactivated", this, panel);
37991         this.fireEvent("invalidated");
37992     },
37993     
37994     /**
37995      * Show the specified panel.
37996      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37997      * @return {Roo.ContentPanel} The shown panel or null
37998      */
37999     showPanel : function(panel){
38000         panel = this.getPanel(panel);
38001         if(panel){
38002             this.setActivePanel(panel);
38003         }
38004         return panel;
38005     },
38006     
38007     /**
38008      * Get the active panel for this region.
38009      * @return {Roo.ContentPanel} The active panel or null
38010      */
38011     getActivePanel : function(){
38012         return this.activePanel;
38013     },
38014     
38015     /**
38016      * Add the passed ContentPanel(s)
38017      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38018      * @return {Roo.ContentPanel} The panel added (if only one was added)
38019      */
38020     add : function(panel){
38021         if(arguments.length > 1){
38022             for(var i = 0, len = arguments.length; i < len; i++) {
38023                 this.add(arguments[i]);
38024             }
38025             return null;
38026         }
38027         if(this.hasPanel(panel)){
38028             this.showPanel(panel);
38029             return panel;
38030         }
38031         var el = panel.getEl();
38032         if(el.dom.parentNode != this.mgr.el.dom){
38033             this.mgr.el.dom.appendChild(el.dom);
38034         }
38035         if(panel.setRegion){
38036             panel.setRegion(this);
38037         }
38038         this.panels.add(panel);
38039         el.setStyle("position", "absolute");
38040         if(!panel.background){
38041             this.setActivePanel(panel);
38042             if(this.config.initialSize && this.panels.getCount()==1){
38043                 this.resizeTo(this.config.initialSize);
38044             }
38045         }
38046         this.fireEvent("paneladded", this, panel);
38047         return panel;
38048     },
38049     
38050     /**
38051      * Returns true if the panel is in this region.
38052      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38053      * @return {Boolean}
38054      */
38055     hasPanel : function(panel){
38056         if(typeof panel == "object"){ // must be panel obj
38057             panel = panel.getId();
38058         }
38059         return this.getPanel(panel) ? true : false;
38060     },
38061     
38062     /**
38063      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38064      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38065      * @param {Boolean} preservePanel Overrides the config preservePanel option
38066      * @return {Roo.ContentPanel} The panel that was removed
38067      */
38068     remove : function(panel, preservePanel){
38069         panel = this.getPanel(panel);
38070         if(!panel){
38071             return null;
38072         }
38073         var e = {};
38074         this.fireEvent("beforeremove", this, panel, e);
38075         if(e.cancel === true){
38076             return null;
38077         }
38078         var panelId = panel.getId();
38079         this.panels.removeKey(panelId);
38080         return panel;
38081     },
38082     
38083     /**
38084      * Returns the panel specified or null if it's not in this region.
38085      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38086      * @return {Roo.ContentPanel}
38087      */
38088     getPanel : function(id){
38089         if(typeof id == "object"){ // must be panel obj
38090             return id;
38091         }
38092         return this.panels.get(id);
38093     },
38094     
38095     /**
38096      * Returns this regions position (north/south/east/west/center).
38097      * @return {String} 
38098      */
38099     getPosition: function(){
38100         return this.position;    
38101     }
38102 });/*
38103  * Based on:
38104  * Ext JS Library 1.1.1
38105  * Copyright(c) 2006-2007, Ext JS, LLC.
38106  *
38107  * Originally Released Under LGPL - original licence link has changed is not relivant.
38108  *
38109  * Fork - LGPL
38110  * <script type="text/javascript">
38111  */
38112  
38113 /**
38114  * @class Roo.bootstrap.layout.Region
38115  * @extends Roo.bootstrap.layout.Basic
38116  * This class represents a region in a layout manager.
38117  
38118  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38119  * @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})
38120  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38121  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38122  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38123  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38124  * @cfg {String}    title           The title for the region (overrides panel titles)
38125  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38126  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38127  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38128  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38129  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38130  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38131  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38132  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38133  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38134  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38135
38136  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38137  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38138  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38139  * @cfg {Number}    width           For East/West panels
38140  * @cfg {Number}    height          For North/South panels
38141  * @cfg {Boolean}   split           To show the splitter
38142  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38143  * 
38144  * @cfg {string}   cls             Extra CSS classes to add to region
38145  * 
38146  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38147  * @cfg {string}   region  the region that it inhabits..
38148  *
38149
38150  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38151  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38152
38153  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38154  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38155  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38156  */
38157 Roo.bootstrap.layout.Region = function(config)
38158 {
38159     this.applyConfig(config);
38160
38161     var mgr = config.mgr;
38162     var pos = config.region;
38163     config.skipConfig = true;
38164     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38165     
38166     if (mgr.el) {
38167         this.onRender(mgr.el);   
38168     }
38169      
38170     this.visible = true;
38171     this.collapsed = false;
38172     this.unrendered_panels = [];
38173 };
38174
38175 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38176
38177     position: '', // set by wrapper (eg. north/south etc..)
38178     unrendered_panels : null,  // unrendered panels.
38179     
38180     tabPosition : false,
38181     
38182     mgr: false, // points to 'Border'
38183     
38184     
38185     createBody : function(){
38186         /** This region's body element 
38187         * @type Roo.Element */
38188         this.bodyEl = this.el.createChild({
38189                 tag: "div",
38190                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38191         });
38192     },
38193
38194     onRender: function(ctr, pos)
38195     {
38196         var dh = Roo.DomHelper;
38197         /** This region's container element 
38198         * @type Roo.Element */
38199         this.el = dh.append(ctr.dom, {
38200                 tag: "div",
38201                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38202             }, true);
38203         /** This region's title element 
38204         * @type Roo.Element */
38205     
38206         this.titleEl = dh.append(this.el.dom,  {
38207                 tag: "div",
38208                 unselectable: "on",
38209                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38210                 children:[
38211                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38212                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38213                 ]
38214             }, true);
38215         
38216         this.titleEl.enableDisplayMode();
38217         /** This region's title text element 
38218         * @type HTMLElement */
38219         this.titleTextEl = this.titleEl.dom.firstChild;
38220         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38221         /*
38222         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38223         this.closeBtn.enableDisplayMode();
38224         this.closeBtn.on("click", this.closeClicked, this);
38225         this.closeBtn.hide();
38226     */
38227         this.createBody(this.config);
38228         if(this.config.hideWhenEmpty){
38229             this.hide();
38230             this.on("paneladded", this.validateVisibility, this);
38231             this.on("panelremoved", this.validateVisibility, this);
38232         }
38233         if(this.autoScroll){
38234             this.bodyEl.setStyle("overflow", "auto");
38235         }else{
38236             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38237         }
38238         //if(c.titlebar !== false){
38239             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38240                 this.titleEl.hide();
38241             }else{
38242                 this.titleEl.show();
38243                 if(this.config.title){
38244                     this.titleTextEl.innerHTML = this.config.title;
38245                 }
38246             }
38247         //}
38248         if(this.config.collapsed){
38249             this.collapse(true);
38250         }
38251         if(this.config.hidden){
38252             this.hide();
38253         }
38254         
38255         if (this.unrendered_panels && this.unrendered_panels.length) {
38256             for (var i =0;i< this.unrendered_panels.length; i++) {
38257                 this.add(this.unrendered_panels[i]);
38258             }
38259             this.unrendered_panels = null;
38260             
38261         }
38262         
38263     },
38264     
38265     applyConfig : function(c)
38266     {
38267         /*
38268          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38269             var dh = Roo.DomHelper;
38270             if(c.titlebar !== false){
38271                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38272                 this.collapseBtn.on("click", this.collapse, this);
38273                 this.collapseBtn.enableDisplayMode();
38274                 /*
38275                 if(c.showPin === true || this.showPin){
38276                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38277                     this.stickBtn.enableDisplayMode();
38278                     this.stickBtn.on("click", this.expand, this);
38279                     this.stickBtn.hide();
38280                 }
38281                 
38282             }
38283             */
38284             /** This region's collapsed element
38285             * @type Roo.Element */
38286             /*
38287              *
38288             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38289                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38290             ]}, true);
38291             
38292             if(c.floatable !== false){
38293                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38294                this.collapsedEl.on("click", this.collapseClick, this);
38295             }
38296
38297             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38298                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38299                    id: "message", unselectable: "on", style:{"float":"left"}});
38300                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38301              }
38302             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38303             this.expandBtn.on("click", this.expand, this);
38304             
38305         }
38306         
38307         if(this.collapseBtn){
38308             this.collapseBtn.setVisible(c.collapsible == true);
38309         }
38310         
38311         this.cmargins = c.cmargins || this.cmargins ||
38312                          (this.position == "west" || this.position == "east" ?
38313                              {top: 0, left: 2, right:2, bottom: 0} :
38314                              {top: 2, left: 0, right:0, bottom: 2});
38315         */
38316         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38317         
38318         
38319         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38320         
38321         this.autoScroll = c.autoScroll || false;
38322         
38323         
38324        
38325         
38326         this.duration = c.duration || .30;
38327         this.slideDuration = c.slideDuration || .45;
38328         this.config = c;
38329        
38330     },
38331     /**
38332      * Returns true if this region is currently visible.
38333      * @return {Boolean}
38334      */
38335     isVisible : function(){
38336         return this.visible;
38337     },
38338
38339     /**
38340      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38341      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38342      */
38343     //setCollapsedTitle : function(title){
38344     //    title = title || "&#160;";
38345      //   if(this.collapsedTitleTextEl){
38346       //      this.collapsedTitleTextEl.innerHTML = title;
38347        // }
38348     //},
38349
38350     getBox : function(){
38351         var b;
38352       //  if(!this.collapsed){
38353             b = this.el.getBox(false, true);
38354        // }else{
38355           //  b = this.collapsedEl.getBox(false, true);
38356         //}
38357         return b;
38358     },
38359
38360     getMargins : function(){
38361         return this.margins;
38362         //return this.collapsed ? this.cmargins : this.margins;
38363     },
38364 /*
38365     highlight : function(){
38366         this.el.addClass("x-layout-panel-dragover");
38367     },
38368
38369     unhighlight : function(){
38370         this.el.removeClass("x-layout-panel-dragover");
38371     },
38372 */
38373     updateBox : function(box)
38374     {
38375         if (!this.bodyEl) {
38376             return; // not rendered yet..
38377         }
38378         
38379         this.box = box;
38380         if(!this.collapsed){
38381             this.el.dom.style.left = box.x + "px";
38382             this.el.dom.style.top = box.y + "px";
38383             this.updateBody(box.width, box.height);
38384         }else{
38385             this.collapsedEl.dom.style.left = box.x + "px";
38386             this.collapsedEl.dom.style.top = box.y + "px";
38387             this.collapsedEl.setSize(box.width, box.height);
38388         }
38389         if(this.tabs){
38390             this.tabs.autoSizeTabs();
38391         }
38392     },
38393
38394     updateBody : function(w, h)
38395     {
38396         if(w !== null){
38397             this.el.setWidth(w);
38398             w -= this.el.getBorderWidth("rl");
38399             if(this.config.adjustments){
38400                 w += this.config.adjustments[0];
38401             }
38402         }
38403         if(h !== null && h > 0){
38404             this.el.setHeight(h);
38405             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38406             h -= this.el.getBorderWidth("tb");
38407             if(this.config.adjustments){
38408                 h += this.config.adjustments[1];
38409             }
38410             this.bodyEl.setHeight(h);
38411             if(this.tabs){
38412                 h = this.tabs.syncHeight(h);
38413             }
38414         }
38415         if(this.panelSize){
38416             w = w !== null ? w : this.panelSize.width;
38417             h = h !== null ? h : this.panelSize.height;
38418         }
38419         if(this.activePanel){
38420             var el = this.activePanel.getEl();
38421             w = w !== null ? w : el.getWidth();
38422             h = h !== null ? h : el.getHeight();
38423             this.panelSize = {width: w, height: h};
38424             this.activePanel.setSize(w, h);
38425         }
38426         if(Roo.isIE && this.tabs){
38427             this.tabs.el.repaint();
38428         }
38429     },
38430
38431     /**
38432      * Returns the container element for this region.
38433      * @return {Roo.Element}
38434      */
38435     getEl : function(){
38436         return this.el;
38437     },
38438
38439     /**
38440      * Hides this region.
38441      */
38442     hide : function(){
38443         //if(!this.collapsed){
38444             this.el.dom.style.left = "-2000px";
38445             this.el.hide();
38446         //}else{
38447          //   this.collapsedEl.dom.style.left = "-2000px";
38448          //   this.collapsedEl.hide();
38449        // }
38450         this.visible = false;
38451         this.fireEvent("visibilitychange", this, false);
38452     },
38453
38454     /**
38455      * Shows this region if it was previously hidden.
38456      */
38457     show : function(){
38458         //if(!this.collapsed){
38459             this.el.show();
38460         //}else{
38461         //    this.collapsedEl.show();
38462        // }
38463         this.visible = true;
38464         this.fireEvent("visibilitychange", this, true);
38465     },
38466 /*
38467     closeClicked : function(){
38468         if(this.activePanel){
38469             this.remove(this.activePanel);
38470         }
38471     },
38472
38473     collapseClick : function(e){
38474         if(this.isSlid){
38475            e.stopPropagation();
38476            this.slideIn();
38477         }else{
38478            e.stopPropagation();
38479            this.slideOut();
38480         }
38481     },
38482 */
38483     /**
38484      * Collapses this region.
38485      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38486      */
38487     /*
38488     collapse : function(skipAnim, skipCheck = false){
38489         if(this.collapsed) {
38490             return;
38491         }
38492         
38493         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38494             
38495             this.collapsed = true;
38496             if(this.split){
38497                 this.split.el.hide();
38498             }
38499             if(this.config.animate && skipAnim !== true){
38500                 this.fireEvent("invalidated", this);
38501                 this.animateCollapse();
38502             }else{
38503                 this.el.setLocation(-20000,-20000);
38504                 this.el.hide();
38505                 this.collapsedEl.show();
38506                 this.fireEvent("collapsed", this);
38507                 this.fireEvent("invalidated", this);
38508             }
38509         }
38510         
38511     },
38512 */
38513     animateCollapse : function(){
38514         // overridden
38515     },
38516
38517     /**
38518      * Expands this region if it was previously collapsed.
38519      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38520      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38521      */
38522     /*
38523     expand : function(e, skipAnim){
38524         if(e) {
38525             e.stopPropagation();
38526         }
38527         if(!this.collapsed || this.el.hasActiveFx()) {
38528             return;
38529         }
38530         if(this.isSlid){
38531             this.afterSlideIn();
38532             skipAnim = true;
38533         }
38534         this.collapsed = false;
38535         if(this.config.animate && skipAnim !== true){
38536             this.animateExpand();
38537         }else{
38538             this.el.show();
38539             if(this.split){
38540                 this.split.el.show();
38541             }
38542             this.collapsedEl.setLocation(-2000,-2000);
38543             this.collapsedEl.hide();
38544             this.fireEvent("invalidated", this);
38545             this.fireEvent("expanded", this);
38546         }
38547     },
38548 */
38549     animateExpand : function(){
38550         // overridden
38551     },
38552
38553     initTabs : function()
38554     {
38555         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38556         
38557         var ts = new Roo.bootstrap.panel.Tabs({
38558             el: this.bodyEl.dom,
38559             region : this,
38560             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38561             disableTooltips: this.config.disableTabTips,
38562             toolbar : this.config.toolbar
38563         });
38564         
38565         if(this.config.hideTabs){
38566             ts.stripWrap.setDisplayed(false);
38567         }
38568         this.tabs = ts;
38569         ts.resizeTabs = this.config.resizeTabs === true;
38570         ts.minTabWidth = this.config.minTabWidth || 40;
38571         ts.maxTabWidth = this.config.maxTabWidth || 250;
38572         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38573         ts.monitorResize = false;
38574         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38575         ts.bodyEl.addClass('roo-layout-tabs-body');
38576         this.panels.each(this.initPanelAsTab, this);
38577     },
38578
38579     initPanelAsTab : function(panel){
38580         var ti = this.tabs.addTab(
38581             panel.getEl().id,
38582             panel.getTitle(),
38583             null,
38584             this.config.closeOnTab && panel.isClosable(),
38585             panel.tpl
38586         );
38587         if(panel.tabTip !== undefined){
38588             ti.setTooltip(panel.tabTip);
38589         }
38590         ti.on("activate", function(){
38591               this.setActivePanel(panel);
38592         }, this);
38593         
38594         if(this.config.closeOnTab){
38595             ti.on("beforeclose", function(t, e){
38596                 e.cancel = true;
38597                 this.remove(panel);
38598             }, this);
38599         }
38600         
38601         panel.tabItem = ti;
38602         
38603         return ti;
38604     },
38605
38606     updatePanelTitle : function(panel, title)
38607     {
38608         if(this.activePanel == panel){
38609             this.updateTitle(title);
38610         }
38611         if(this.tabs){
38612             var ti = this.tabs.getTab(panel.getEl().id);
38613             ti.setText(title);
38614             if(panel.tabTip !== undefined){
38615                 ti.setTooltip(panel.tabTip);
38616             }
38617         }
38618     },
38619
38620     updateTitle : function(title){
38621         if(this.titleTextEl && !this.config.title){
38622             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38623         }
38624     },
38625
38626     setActivePanel : function(panel)
38627     {
38628         panel = this.getPanel(panel);
38629         if(this.activePanel && this.activePanel != panel){
38630             if(this.activePanel.setActiveState(false) === false){
38631                 return;
38632             }
38633         }
38634         this.activePanel = panel;
38635         panel.setActiveState(true);
38636         if(this.panelSize){
38637             panel.setSize(this.panelSize.width, this.panelSize.height);
38638         }
38639         if(this.closeBtn){
38640             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38641         }
38642         this.updateTitle(panel.getTitle());
38643         if(this.tabs){
38644             this.fireEvent("invalidated", this);
38645         }
38646         this.fireEvent("panelactivated", this, panel);
38647     },
38648
38649     /**
38650      * Shows the specified panel.
38651      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38652      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38653      */
38654     showPanel : function(panel)
38655     {
38656         panel = this.getPanel(panel);
38657         if(panel){
38658             if(this.tabs){
38659                 var tab = this.tabs.getTab(panel.getEl().id);
38660                 if(tab.isHidden()){
38661                     this.tabs.unhideTab(tab.id);
38662                 }
38663                 tab.activate();
38664             }else{
38665                 this.setActivePanel(panel);
38666             }
38667         }
38668         return panel;
38669     },
38670
38671     /**
38672      * Get the active panel for this region.
38673      * @return {Roo.ContentPanel} The active panel or null
38674      */
38675     getActivePanel : function(){
38676         return this.activePanel;
38677     },
38678
38679     validateVisibility : function(){
38680         if(this.panels.getCount() < 1){
38681             this.updateTitle("&#160;");
38682             this.closeBtn.hide();
38683             this.hide();
38684         }else{
38685             if(!this.isVisible()){
38686                 this.show();
38687             }
38688         }
38689     },
38690
38691     /**
38692      * Adds the passed ContentPanel(s) to this region.
38693      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38694      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38695      */
38696     add : function(panel)
38697     {
38698         if(arguments.length > 1){
38699             for(var i = 0, len = arguments.length; i < len; i++) {
38700                 this.add(arguments[i]);
38701             }
38702             return null;
38703         }
38704         
38705         // if we have not been rendered yet, then we can not really do much of this..
38706         if (!this.bodyEl) {
38707             this.unrendered_panels.push(panel);
38708             return panel;
38709         }
38710         
38711         
38712         
38713         
38714         if(this.hasPanel(panel)){
38715             this.showPanel(panel);
38716             return panel;
38717         }
38718         panel.setRegion(this);
38719         this.panels.add(panel);
38720        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38721             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38722             // and hide them... ???
38723             this.bodyEl.dom.appendChild(panel.getEl().dom);
38724             if(panel.background !== true){
38725                 this.setActivePanel(panel);
38726             }
38727             this.fireEvent("paneladded", this, panel);
38728             return panel;
38729         }
38730         */
38731         if(!this.tabs){
38732             this.initTabs();
38733         }else{
38734             this.initPanelAsTab(panel);
38735         }
38736         
38737         
38738         if(panel.background !== true){
38739             this.tabs.activate(panel.getEl().id);
38740         }
38741         this.fireEvent("paneladded", this, panel);
38742         return panel;
38743     },
38744
38745     /**
38746      * Hides the tab for the specified panel.
38747      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38748      */
38749     hidePanel : function(panel){
38750         if(this.tabs && (panel = this.getPanel(panel))){
38751             this.tabs.hideTab(panel.getEl().id);
38752         }
38753     },
38754
38755     /**
38756      * Unhides the tab for a previously hidden panel.
38757      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38758      */
38759     unhidePanel : function(panel){
38760         if(this.tabs && (panel = this.getPanel(panel))){
38761             this.tabs.unhideTab(panel.getEl().id);
38762         }
38763     },
38764
38765     clearPanels : function(){
38766         while(this.panels.getCount() > 0){
38767              this.remove(this.panels.first());
38768         }
38769     },
38770
38771     /**
38772      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38773      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38774      * @param {Boolean} preservePanel Overrides the config preservePanel option
38775      * @return {Roo.ContentPanel} The panel that was removed
38776      */
38777     remove : function(panel, preservePanel)
38778     {
38779         panel = this.getPanel(panel);
38780         if(!panel){
38781             return null;
38782         }
38783         var e = {};
38784         this.fireEvent("beforeremove", this, panel, e);
38785         if(e.cancel === true){
38786             return null;
38787         }
38788         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38789         var panelId = panel.getId();
38790         this.panels.removeKey(panelId);
38791         if(preservePanel){
38792             document.body.appendChild(panel.getEl().dom);
38793         }
38794         if(this.tabs){
38795             this.tabs.removeTab(panel.getEl().id);
38796         }else if (!preservePanel){
38797             this.bodyEl.dom.removeChild(panel.getEl().dom);
38798         }
38799         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38800             var p = this.panels.first();
38801             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38802             tempEl.appendChild(p.getEl().dom);
38803             this.bodyEl.update("");
38804             this.bodyEl.dom.appendChild(p.getEl().dom);
38805             tempEl = null;
38806             this.updateTitle(p.getTitle());
38807             this.tabs = null;
38808             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38809             this.setActivePanel(p);
38810         }
38811         panel.setRegion(null);
38812         if(this.activePanel == panel){
38813             this.activePanel = null;
38814         }
38815         if(this.config.autoDestroy !== false && preservePanel !== true){
38816             try{panel.destroy();}catch(e){}
38817         }
38818         this.fireEvent("panelremoved", this, panel);
38819         return panel;
38820     },
38821
38822     /**
38823      * Returns the TabPanel component used by this region
38824      * @return {Roo.TabPanel}
38825      */
38826     getTabs : function(){
38827         return this.tabs;
38828     },
38829
38830     createTool : function(parentEl, className){
38831         var btn = Roo.DomHelper.append(parentEl, {
38832             tag: "div",
38833             cls: "x-layout-tools-button",
38834             children: [ {
38835                 tag: "div",
38836                 cls: "roo-layout-tools-button-inner " + className,
38837                 html: "&#160;"
38838             }]
38839         }, true);
38840         btn.addClassOnOver("roo-layout-tools-button-over");
38841         return btn;
38842     }
38843 });/*
38844  * Based on:
38845  * Ext JS Library 1.1.1
38846  * Copyright(c) 2006-2007, Ext JS, LLC.
38847  *
38848  * Originally Released Under LGPL - original licence link has changed is not relivant.
38849  *
38850  * Fork - LGPL
38851  * <script type="text/javascript">
38852  */
38853  
38854
38855
38856 /**
38857  * @class Roo.SplitLayoutRegion
38858  * @extends Roo.LayoutRegion
38859  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38860  */
38861 Roo.bootstrap.layout.Split = function(config){
38862     this.cursor = config.cursor;
38863     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38864 };
38865
38866 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38867 {
38868     splitTip : "Drag to resize.",
38869     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38870     useSplitTips : false,
38871
38872     applyConfig : function(config){
38873         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38874     },
38875     
38876     onRender : function(ctr,pos) {
38877         
38878         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38879         if(!this.config.split){
38880             return;
38881         }
38882         if(!this.split){
38883             
38884             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38885                             tag: "div",
38886                             id: this.el.id + "-split",
38887                             cls: "roo-layout-split roo-layout-split-"+this.position,
38888                             html: "&#160;"
38889             });
38890             /** The SplitBar for this region 
38891             * @type Roo.SplitBar */
38892             // does not exist yet...
38893             Roo.log([this.position, this.orientation]);
38894             
38895             this.split = new Roo.bootstrap.SplitBar({
38896                 dragElement : splitEl,
38897                 resizingElement: this.el,
38898                 orientation : this.orientation
38899             });
38900             
38901             this.split.on("moved", this.onSplitMove, this);
38902             this.split.useShim = this.config.useShim === true;
38903             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38904             if(this.useSplitTips){
38905                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38906             }
38907             //if(config.collapsible){
38908             //    this.split.el.on("dblclick", this.collapse,  this);
38909             //}
38910         }
38911         if(typeof this.config.minSize != "undefined"){
38912             this.split.minSize = this.config.minSize;
38913         }
38914         if(typeof this.config.maxSize != "undefined"){
38915             this.split.maxSize = this.config.maxSize;
38916         }
38917         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38918             this.hideSplitter();
38919         }
38920         
38921     },
38922
38923     getHMaxSize : function(){
38924          var cmax = this.config.maxSize || 10000;
38925          var center = this.mgr.getRegion("center");
38926          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38927     },
38928
38929     getVMaxSize : function(){
38930          var cmax = this.config.maxSize || 10000;
38931          var center = this.mgr.getRegion("center");
38932          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38933     },
38934
38935     onSplitMove : function(split, newSize){
38936         this.fireEvent("resized", this, newSize);
38937     },
38938     
38939     /** 
38940      * Returns the {@link Roo.SplitBar} for this region.
38941      * @return {Roo.SplitBar}
38942      */
38943     getSplitBar : function(){
38944         return this.split;
38945     },
38946     
38947     hide : function(){
38948         this.hideSplitter();
38949         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38950     },
38951
38952     hideSplitter : function(){
38953         if(this.split){
38954             this.split.el.setLocation(-2000,-2000);
38955             this.split.el.hide();
38956         }
38957     },
38958
38959     show : function(){
38960         if(this.split){
38961             this.split.el.show();
38962         }
38963         Roo.bootstrap.layout.Split.superclass.show.call(this);
38964     },
38965     
38966     beforeSlide: function(){
38967         if(Roo.isGecko){// firefox overflow auto bug workaround
38968             this.bodyEl.clip();
38969             if(this.tabs) {
38970                 this.tabs.bodyEl.clip();
38971             }
38972             if(this.activePanel){
38973                 this.activePanel.getEl().clip();
38974                 
38975                 if(this.activePanel.beforeSlide){
38976                     this.activePanel.beforeSlide();
38977                 }
38978             }
38979         }
38980     },
38981     
38982     afterSlide : function(){
38983         if(Roo.isGecko){// firefox overflow auto bug workaround
38984             this.bodyEl.unclip();
38985             if(this.tabs) {
38986                 this.tabs.bodyEl.unclip();
38987             }
38988             if(this.activePanel){
38989                 this.activePanel.getEl().unclip();
38990                 if(this.activePanel.afterSlide){
38991                     this.activePanel.afterSlide();
38992                 }
38993             }
38994         }
38995     },
38996
38997     initAutoHide : function(){
38998         if(this.autoHide !== false){
38999             if(!this.autoHideHd){
39000                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39001                 this.autoHideHd = {
39002                     "mouseout": function(e){
39003                         if(!e.within(this.el, true)){
39004                             st.delay(500);
39005                         }
39006                     },
39007                     "mouseover" : function(e){
39008                         st.cancel();
39009                     },
39010                     scope : this
39011                 };
39012             }
39013             this.el.on(this.autoHideHd);
39014         }
39015     },
39016
39017     clearAutoHide : function(){
39018         if(this.autoHide !== false){
39019             this.el.un("mouseout", this.autoHideHd.mouseout);
39020             this.el.un("mouseover", this.autoHideHd.mouseover);
39021         }
39022     },
39023
39024     clearMonitor : function(){
39025         Roo.get(document).un("click", this.slideInIf, this);
39026     },
39027
39028     // these names are backwards but not changed for compat
39029     slideOut : function(){
39030         if(this.isSlid || this.el.hasActiveFx()){
39031             return;
39032         }
39033         this.isSlid = true;
39034         if(this.collapseBtn){
39035             this.collapseBtn.hide();
39036         }
39037         this.closeBtnState = this.closeBtn.getStyle('display');
39038         this.closeBtn.hide();
39039         if(this.stickBtn){
39040             this.stickBtn.show();
39041         }
39042         this.el.show();
39043         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39044         this.beforeSlide();
39045         this.el.setStyle("z-index", 10001);
39046         this.el.slideIn(this.getSlideAnchor(), {
39047             callback: function(){
39048                 this.afterSlide();
39049                 this.initAutoHide();
39050                 Roo.get(document).on("click", this.slideInIf, this);
39051                 this.fireEvent("slideshow", this);
39052             },
39053             scope: this,
39054             block: true
39055         });
39056     },
39057
39058     afterSlideIn : function(){
39059         this.clearAutoHide();
39060         this.isSlid = false;
39061         this.clearMonitor();
39062         this.el.setStyle("z-index", "");
39063         if(this.collapseBtn){
39064             this.collapseBtn.show();
39065         }
39066         this.closeBtn.setStyle('display', this.closeBtnState);
39067         if(this.stickBtn){
39068             this.stickBtn.hide();
39069         }
39070         this.fireEvent("slidehide", this);
39071     },
39072
39073     slideIn : function(cb){
39074         if(!this.isSlid || this.el.hasActiveFx()){
39075             Roo.callback(cb);
39076             return;
39077         }
39078         this.isSlid = false;
39079         this.beforeSlide();
39080         this.el.slideOut(this.getSlideAnchor(), {
39081             callback: function(){
39082                 this.el.setLeftTop(-10000, -10000);
39083                 this.afterSlide();
39084                 this.afterSlideIn();
39085                 Roo.callback(cb);
39086             },
39087             scope: this,
39088             block: true
39089         });
39090     },
39091     
39092     slideInIf : function(e){
39093         if(!e.within(this.el)){
39094             this.slideIn();
39095         }
39096     },
39097
39098     animateCollapse : function(){
39099         this.beforeSlide();
39100         this.el.setStyle("z-index", 20000);
39101         var anchor = this.getSlideAnchor();
39102         this.el.slideOut(anchor, {
39103             callback : function(){
39104                 this.el.setStyle("z-index", "");
39105                 this.collapsedEl.slideIn(anchor, {duration:.3});
39106                 this.afterSlide();
39107                 this.el.setLocation(-10000,-10000);
39108                 this.el.hide();
39109                 this.fireEvent("collapsed", this);
39110             },
39111             scope: this,
39112             block: true
39113         });
39114     },
39115
39116     animateExpand : function(){
39117         this.beforeSlide();
39118         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39119         this.el.setStyle("z-index", 20000);
39120         this.collapsedEl.hide({
39121             duration:.1
39122         });
39123         this.el.slideIn(this.getSlideAnchor(), {
39124             callback : function(){
39125                 this.el.setStyle("z-index", "");
39126                 this.afterSlide();
39127                 if(this.split){
39128                     this.split.el.show();
39129                 }
39130                 this.fireEvent("invalidated", this);
39131                 this.fireEvent("expanded", this);
39132             },
39133             scope: this,
39134             block: true
39135         });
39136     },
39137
39138     anchors : {
39139         "west" : "left",
39140         "east" : "right",
39141         "north" : "top",
39142         "south" : "bottom"
39143     },
39144
39145     sanchors : {
39146         "west" : "l",
39147         "east" : "r",
39148         "north" : "t",
39149         "south" : "b"
39150     },
39151
39152     canchors : {
39153         "west" : "tl-tr",
39154         "east" : "tr-tl",
39155         "north" : "tl-bl",
39156         "south" : "bl-tl"
39157     },
39158
39159     getAnchor : function(){
39160         return this.anchors[this.position];
39161     },
39162
39163     getCollapseAnchor : function(){
39164         return this.canchors[this.position];
39165     },
39166
39167     getSlideAnchor : function(){
39168         return this.sanchors[this.position];
39169     },
39170
39171     getAlignAdj : function(){
39172         var cm = this.cmargins;
39173         switch(this.position){
39174             case "west":
39175                 return [0, 0];
39176             break;
39177             case "east":
39178                 return [0, 0];
39179             break;
39180             case "north":
39181                 return [0, 0];
39182             break;
39183             case "south":
39184                 return [0, 0];
39185             break;
39186         }
39187     },
39188
39189     getExpandAdj : function(){
39190         var c = this.collapsedEl, cm = this.cmargins;
39191         switch(this.position){
39192             case "west":
39193                 return [-(cm.right+c.getWidth()+cm.left), 0];
39194             break;
39195             case "east":
39196                 return [cm.right+c.getWidth()+cm.left, 0];
39197             break;
39198             case "north":
39199                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39200             break;
39201             case "south":
39202                 return [0, cm.top+cm.bottom+c.getHeight()];
39203             break;
39204         }
39205     }
39206 });/*
39207  * Based on:
39208  * Ext JS Library 1.1.1
39209  * Copyright(c) 2006-2007, Ext JS, LLC.
39210  *
39211  * Originally Released Under LGPL - original licence link has changed is not relivant.
39212  *
39213  * Fork - LGPL
39214  * <script type="text/javascript">
39215  */
39216 /*
39217  * These classes are private internal classes
39218  */
39219 Roo.bootstrap.layout.Center = function(config){
39220     config.region = "center";
39221     Roo.bootstrap.layout.Region.call(this, config);
39222     this.visible = true;
39223     this.minWidth = config.minWidth || 20;
39224     this.minHeight = config.minHeight || 20;
39225 };
39226
39227 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39228     hide : function(){
39229         // center panel can't be hidden
39230     },
39231     
39232     show : function(){
39233         // center panel can't be hidden
39234     },
39235     
39236     getMinWidth: function(){
39237         return this.minWidth;
39238     },
39239     
39240     getMinHeight: function(){
39241         return this.minHeight;
39242     }
39243 });
39244
39245
39246
39247
39248  
39249
39250
39251
39252
39253
39254
39255 Roo.bootstrap.layout.North = function(config)
39256 {
39257     config.region = 'north';
39258     config.cursor = 'n-resize';
39259     
39260     Roo.bootstrap.layout.Split.call(this, config);
39261     
39262     
39263     if(this.split){
39264         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39265         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39266         this.split.el.addClass("roo-layout-split-v");
39267     }
39268     var size = config.initialSize || config.height;
39269     if(typeof size != "undefined"){
39270         this.el.setHeight(size);
39271     }
39272 };
39273 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39274 {
39275     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39276     
39277     
39278     
39279     getBox : function(){
39280         if(this.collapsed){
39281             return this.collapsedEl.getBox();
39282         }
39283         var box = this.el.getBox();
39284         if(this.split){
39285             box.height += this.split.el.getHeight();
39286         }
39287         return box;
39288     },
39289     
39290     updateBox : function(box){
39291         if(this.split && !this.collapsed){
39292             box.height -= this.split.el.getHeight();
39293             this.split.el.setLeft(box.x);
39294             this.split.el.setTop(box.y+box.height);
39295             this.split.el.setWidth(box.width);
39296         }
39297         if(this.collapsed){
39298             this.updateBody(box.width, null);
39299         }
39300         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39301     }
39302 });
39303
39304
39305
39306
39307
39308 Roo.bootstrap.layout.South = function(config){
39309     config.region = 'south';
39310     config.cursor = 's-resize';
39311     Roo.bootstrap.layout.Split.call(this, config);
39312     if(this.split){
39313         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39314         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39315         this.split.el.addClass("roo-layout-split-v");
39316     }
39317     var size = config.initialSize || config.height;
39318     if(typeof size != "undefined"){
39319         this.el.setHeight(size);
39320     }
39321 };
39322
39323 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39324     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39325     getBox : function(){
39326         if(this.collapsed){
39327             return this.collapsedEl.getBox();
39328         }
39329         var box = this.el.getBox();
39330         if(this.split){
39331             var sh = this.split.el.getHeight();
39332             box.height += sh;
39333             box.y -= sh;
39334         }
39335         return box;
39336     },
39337     
39338     updateBox : function(box){
39339         if(this.split && !this.collapsed){
39340             var sh = this.split.el.getHeight();
39341             box.height -= sh;
39342             box.y += sh;
39343             this.split.el.setLeft(box.x);
39344             this.split.el.setTop(box.y-sh);
39345             this.split.el.setWidth(box.width);
39346         }
39347         if(this.collapsed){
39348             this.updateBody(box.width, null);
39349         }
39350         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39351     }
39352 });
39353
39354 Roo.bootstrap.layout.East = function(config){
39355     config.region = "east";
39356     config.cursor = "e-resize";
39357     Roo.bootstrap.layout.Split.call(this, config);
39358     if(this.split){
39359         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39360         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39361         this.split.el.addClass("roo-layout-split-h");
39362     }
39363     var size = config.initialSize || config.width;
39364     if(typeof size != "undefined"){
39365         this.el.setWidth(size);
39366     }
39367 };
39368 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39369     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39370     getBox : function(){
39371         if(this.collapsed){
39372             return this.collapsedEl.getBox();
39373         }
39374         var box = this.el.getBox();
39375         if(this.split){
39376             var sw = this.split.el.getWidth();
39377             box.width += sw;
39378             box.x -= sw;
39379         }
39380         return box;
39381     },
39382
39383     updateBox : function(box){
39384         if(this.split && !this.collapsed){
39385             var sw = this.split.el.getWidth();
39386             box.width -= sw;
39387             this.split.el.setLeft(box.x);
39388             this.split.el.setTop(box.y);
39389             this.split.el.setHeight(box.height);
39390             box.x += sw;
39391         }
39392         if(this.collapsed){
39393             this.updateBody(null, box.height);
39394         }
39395         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39396     }
39397 });
39398
39399 Roo.bootstrap.layout.West = function(config){
39400     config.region = "west";
39401     config.cursor = "w-resize";
39402     
39403     Roo.bootstrap.layout.Split.call(this, config);
39404     if(this.split){
39405         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39406         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39407         this.split.el.addClass("roo-layout-split-h");
39408     }
39409     
39410 };
39411 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39412     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39413     
39414     onRender: function(ctr, pos)
39415     {
39416         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39417         var size = this.config.initialSize || this.config.width;
39418         if(typeof size != "undefined"){
39419             this.el.setWidth(size);
39420         }
39421     },
39422     
39423     getBox : function(){
39424         if(this.collapsed){
39425             return this.collapsedEl.getBox();
39426         }
39427         var box = this.el.getBox();
39428         if(this.split){
39429             box.width += this.split.el.getWidth();
39430         }
39431         return box;
39432     },
39433     
39434     updateBox : function(box){
39435         if(this.split && !this.collapsed){
39436             var sw = this.split.el.getWidth();
39437             box.width -= sw;
39438             this.split.el.setLeft(box.x+box.width);
39439             this.split.el.setTop(box.y);
39440             this.split.el.setHeight(box.height);
39441         }
39442         if(this.collapsed){
39443             this.updateBody(null, box.height);
39444         }
39445         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39446     }
39447 });Roo.namespace("Roo.bootstrap.panel");/*
39448  * Based on:
39449  * Ext JS Library 1.1.1
39450  * Copyright(c) 2006-2007, Ext JS, LLC.
39451  *
39452  * Originally Released Under LGPL - original licence link has changed is not relivant.
39453  *
39454  * Fork - LGPL
39455  * <script type="text/javascript">
39456  */
39457 /**
39458  * @class Roo.ContentPanel
39459  * @extends Roo.util.Observable
39460  * A basic ContentPanel element.
39461  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39462  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39463  * @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
39464  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39465  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39466  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39467  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39468  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39469  * @cfg {String} title          The title for this panel
39470  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39471  * @cfg {String} url            Calls {@link #setUrl} with this value
39472  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39473  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39474  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39475  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39476  * @cfg {Boolean} badges render the badges
39477  * @cfg {String} cls  extra classes to use  
39478  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39479
39480  * @constructor
39481  * Create a new ContentPanel.
39482  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39483  * @param {String/Object} config A string to set only the title or a config object
39484  * @param {String} content (optional) Set the HTML content for this panel
39485  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39486  */
39487 Roo.bootstrap.panel.Content = function( config){
39488     
39489     this.tpl = config.tpl || false;
39490     
39491     var el = config.el;
39492     var content = config.content;
39493
39494     if(config.autoCreate){ // xtype is available if this is called from factory
39495         el = Roo.id();
39496     }
39497     this.el = Roo.get(el);
39498     if(!this.el && config && config.autoCreate){
39499         if(typeof config.autoCreate == "object"){
39500             if(!config.autoCreate.id){
39501                 config.autoCreate.id = config.id||el;
39502             }
39503             this.el = Roo.DomHelper.append(document.body,
39504                         config.autoCreate, true);
39505         }else{
39506             var elcfg =  {
39507                 tag: "div",
39508                 cls: (config.cls || '') +
39509                     (config.background ? ' bg-' + config.background : '') +
39510                     " roo-layout-inactive-content",
39511                 id: config.id||el
39512             };
39513             if (config.html) {
39514                 elcfg.html = config.html;
39515                 
39516             }
39517                         
39518             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39519         }
39520     } 
39521     this.closable = false;
39522     this.loaded = false;
39523     this.active = false;
39524    
39525       
39526     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39527         
39528         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39529         
39530         this.wrapEl = this.el; //this.el.wrap();
39531         var ti = [];
39532         if (config.toolbar.items) {
39533             ti = config.toolbar.items ;
39534             delete config.toolbar.items ;
39535         }
39536         
39537         var nitems = [];
39538         this.toolbar.render(this.wrapEl, 'before');
39539         for(var i =0;i < ti.length;i++) {
39540           //  Roo.log(['add child', items[i]]);
39541             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39542         }
39543         this.toolbar.items = nitems;
39544         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39545         delete config.toolbar;
39546         
39547     }
39548     /*
39549     // xtype created footer. - not sure if will work as we normally have to render first..
39550     if (this.footer && !this.footer.el && this.footer.xtype) {
39551         if (!this.wrapEl) {
39552             this.wrapEl = this.el.wrap();
39553         }
39554     
39555         this.footer.container = this.wrapEl.createChild();
39556          
39557         this.footer = Roo.factory(this.footer, Roo);
39558         
39559     }
39560     */
39561     
39562      if(typeof config == "string"){
39563         this.title = config;
39564     }else{
39565         Roo.apply(this, config);
39566     }
39567     
39568     if(this.resizeEl){
39569         this.resizeEl = Roo.get(this.resizeEl, true);
39570     }else{
39571         this.resizeEl = this.el;
39572     }
39573     // handle view.xtype
39574     
39575  
39576     
39577     
39578     this.addEvents({
39579         /**
39580          * @event activate
39581          * Fires when this panel is activated. 
39582          * @param {Roo.ContentPanel} this
39583          */
39584         "activate" : true,
39585         /**
39586          * @event deactivate
39587          * Fires when this panel is activated. 
39588          * @param {Roo.ContentPanel} this
39589          */
39590         "deactivate" : true,
39591
39592         /**
39593          * @event resize
39594          * Fires when this panel is resized if fitToFrame is true.
39595          * @param {Roo.ContentPanel} this
39596          * @param {Number} width The width after any component adjustments
39597          * @param {Number} height The height after any component adjustments
39598          */
39599         "resize" : true,
39600         
39601          /**
39602          * @event render
39603          * Fires when this tab is created
39604          * @param {Roo.ContentPanel} this
39605          */
39606         "render" : true
39607         
39608         
39609         
39610     });
39611     
39612
39613     
39614     
39615     if(this.autoScroll){
39616         this.resizeEl.setStyle("overflow", "auto");
39617     } else {
39618         // fix randome scrolling
39619         //this.el.on('scroll', function() {
39620         //    Roo.log('fix random scolling');
39621         //    this.scrollTo('top',0); 
39622         //});
39623     }
39624     content = content || this.content;
39625     if(content){
39626         this.setContent(content);
39627     }
39628     if(config && config.url){
39629         this.setUrl(this.url, this.params, this.loadOnce);
39630     }
39631     
39632     
39633     
39634     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39635     
39636     if (this.view && typeof(this.view.xtype) != 'undefined') {
39637         this.view.el = this.el.appendChild(document.createElement("div"));
39638         this.view = Roo.factory(this.view); 
39639         this.view.render  &&  this.view.render(false, '');  
39640     }
39641     
39642     
39643     this.fireEvent('render', this);
39644 };
39645
39646 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39647     
39648     cls : '',
39649     background : '',
39650     
39651     tabTip : '',
39652     
39653     setRegion : function(region){
39654         this.region = region;
39655         this.setActiveClass(region && !this.background);
39656     },
39657     
39658     
39659     setActiveClass: function(state)
39660     {
39661         if(state){
39662            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39663            this.el.setStyle('position','relative');
39664         }else{
39665            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39666            this.el.setStyle('position', 'absolute');
39667         } 
39668     },
39669     
39670     /**
39671      * Returns the toolbar for this Panel if one was configured. 
39672      * @return {Roo.Toolbar} 
39673      */
39674     getToolbar : function(){
39675         return this.toolbar;
39676     },
39677     
39678     setActiveState : function(active)
39679     {
39680         this.active = active;
39681         this.setActiveClass(active);
39682         if(!active){
39683             if(this.fireEvent("deactivate", this) === false){
39684                 return false;
39685             }
39686             return true;
39687         }
39688         this.fireEvent("activate", this);
39689         return true;
39690     },
39691     /**
39692      * Updates this panel's element
39693      * @param {String} content The new content
39694      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39695     */
39696     setContent : function(content, loadScripts){
39697         this.el.update(content, loadScripts);
39698     },
39699
39700     ignoreResize : function(w, h){
39701         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39702             return true;
39703         }else{
39704             this.lastSize = {width: w, height: h};
39705             return false;
39706         }
39707     },
39708     /**
39709      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39710      * @return {Roo.UpdateManager} The UpdateManager
39711      */
39712     getUpdateManager : function(){
39713         return this.el.getUpdateManager();
39714     },
39715      /**
39716      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39717      * @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:
39718 <pre><code>
39719 panel.load({
39720     url: "your-url.php",
39721     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39722     callback: yourFunction,
39723     scope: yourObject, //(optional scope)
39724     discardUrl: false,
39725     nocache: false,
39726     text: "Loading...",
39727     timeout: 30,
39728     scripts: false
39729 });
39730 </code></pre>
39731      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39732      * 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.
39733      * @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}
39734      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39735      * @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.
39736      * @return {Roo.ContentPanel} this
39737      */
39738     load : function(){
39739         var um = this.el.getUpdateManager();
39740         um.update.apply(um, arguments);
39741         return this;
39742     },
39743
39744
39745     /**
39746      * 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.
39747      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39748      * @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)
39749      * @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)
39750      * @return {Roo.UpdateManager} The UpdateManager
39751      */
39752     setUrl : function(url, params, loadOnce){
39753         if(this.refreshDelegate){
39754             this.removeListener("activate", this.refreshDelegate);
39755         }
39756         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39757         this.on("activate", this.refreshDelegate);
39758         return this.el.getUpdateManager();
39759     },
39760     
39761     _handleRefresh : function(url, params, loadOnce){
39762         if(!loadOnce || !this.loaded){
39763             var updater = this.el.getUpdateManager();
39764             updater.update(url, params, this._setLoaded.createDelegate(this));
39765         }
39766     },
39767     
39768     _setLoaded : function(){
39769         this.loaded = true;
39770     }, 
39771     
39772     /**
39773      * Returns this panel's id
39774      * @return {String} 
39775      */
39776     getId : function(){
39777         return this.el.id;
39778     },
39779     
39780     /** 
39781      * Returns this panel's element - used by regiosn to add.
39782      * @return {Roo.Element} 
39783      */
39784     getEl : function(){
39785         return this.wrapEl || this.el;
39786     },
39787     
39788    
39789     
39790     adjustForComponents : function(width, height)
39791     {
39792         //Roo.log('adjustForComponents ');
39793         if(this.resizeEl != this.el){
39794             width -= this.el.getFrameWidth('lr');
39795             height -= this.el.getFrameWidth('tb');
39796         }
39797         if(this.toolbar){
39798             var te = this.toolbar.getEl();
39799             te.setWidth(width);
39800             height -= te.getHeight();
39801         }
39802         if(this.footer){
39803             var te = this.footer.getEl();
39804             te.setWidth(width);
39805             height -= te.getHeight();
39806         }
39807         
39808         
39809         if(this.adjustments){
39810             width += this.adjustments[0];
39811             height += this.adjustments[1];
39812         }
39813         return {"width": width, "height": height};
39814     },
39815     
39816     setSize : function(width, height){
39817         if(this.fitToFrame && !this.ignoreResize(width, height)){
39818             if(this.fitContainer && this.resizeEl != this.el){
39819                 this.el.setSize(width, height);
39820             }
39821             var size = this.adjustForComponents(width, height);
39822             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39823             this.fireEvent('resize', this, size.width, size.height);
39824         }
39825     },
39826     
39827     /**
39828      * Returns this panel's title
39829      * @return {String} 
39830      */
39831     getTitle : function(){
39832         
39833         if (typeof(this.title) != 'object') {
39834             return this.title;
39835         }
39836         
39837         var t = '';
39838         for (var k in this.title) {
39839             if (!this.title.hasOwnProperty(k)) {
39840                 continue;
39841             }
39842             
39843             if (k.indexOf('-') >= 0) {
39844                 var s = k.split('-');
39845                 for (var i = 0; i<s.length; i++) {
39846                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39847                 }
39848             } else {
39849                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39850             }
39851         }
39852         return t;
39853     },
39854     
39855     /**
39856      * Set this panel's title
39857      * @param {String} title
39858      */
39859     setTitle : function(title){
39860         this.title = title;
39861         if(this.region){
39862             this.region.updatePanelTitle(this, title);
39863         }
39864     },
39865     
39866     /**
39867      * Returns true is this panel was configured to be closable
39868      * @return {Boolean} 
39869      */
39870     isClosable : function(){
39871         return this.closable;
39872     },
39873     
39874     beforeSlide : function(){
39875         this.el.clip();
39876         this.resizeEl.clip();
39877     },
39878     
39879     afterSlide : function(){
39880         this.el.unclip();
39881         this.resizeEl.unclip();
39882     },
39883     
39884     /**
39885      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39886      *   Will fail silently if the {@link #setUrl} method has not been called.
39887      *   This does not activate the panel, just updates its content.
39888      */
39889     refresh : function(){
39890         if(this.refreshDelegate){
39891            this.loaded = false;
39892            this.refreshDelegate();
39893         }
39894     },
39895     
39896     /**
39897      * Destroys this panel
39898      */
39899     destroy : function(){
39900         this.el.removeAllListeners();
39901         var tempEl = document.createElement("span");
39902         tempEl.appendChild(this.el.dom);
39903         tempEl.innerHTML = "";
39904         this.el.remove();
39905         this.el = null;
39906     },
39907     
39908     /**
39909      * form - if the content panel contains a form - this is a reference to it.
39910      * @type {Roo.form.Form}
39911      */
39912     form : false,
39913     /**
39914      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39915      *    This contains a reference to it.
39916      * @type {Roo.View}
39917      */
39918     view : false,
39919     
39920       /**
39921      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39922      * <pre><code>
39923
39924 layout.addxtype({
39925        xtype : 'Form',
39926        items: [ .... ]
39927    }
39928 );
39929
39930 </code></pre>
39931      * @param {Object} cfg Xtype definition of item to add.
39932      */
39933     
39934     
39935     getChildContainer: function () {
39936         return this.getEl();
39937     }
39938     
39939     
39940     /*
39941         var  ret = new Roo.factory(cfg);
39942         return ret;
39943         
39944         
39945         // add form..
39946         if (cfg.xtype.match(/^Form$/)) {
39947             
39948             var el;
39949             //if (this.footer) {
39950             //    el = this.footer.container.insertSibling(false, 'before');
39951             //} else {
39952                 el = this.el.createChild();
39953             //}
39954
39955             this.form = new  Roo.form.Form(cfg);
39956             
39957             
39958             if ( this.form.allItems.length) {
39959                 this.form.render(el.dom);
39960             }
39961             return this.form;
39962         }
39963         // should only have one of theses..
39964         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39965             // views.. should not be just added - used named prop 'view''
39966             
39967             cfg.el = this.el.appendChild(document.createElement("div"));
39968             // factory?
39969             
39970             var ret = new Roo.factory(cfg);
39971              
39972              ret.render && ret.render(false, ''); // render blank..
39973             this.view = ret;
39974             return ret;
39975         }
39976         return false;
39977     }
39978     \*/
39979 });
39980  
39981 /**
39982  * @class Roo.bootstrap.panel.Grid
39983  * @extends Roo.bootstrap.panel.Content
39984  * @constructor
39985  * Create a new GridPanel.
39986  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39987  * @param {Object} config A the config object
39988   
39989  */
39990
39991
39992
39993 Roo.bootstrap.panel.Grid = function(config)
39994 {
39995     
39996       
39997     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39998         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39999
40000     config.el = this.wrapper;
40001     //this.el = this.wrapper;
40002     
40003       if (config.container) {
40004         // ctor'ed from a Border/panel.grid
40005         
40006         
40007         this.wrapper.setStyle("overflow", "hidden");
40008         this.wrapper.addClass('roo-grid-container');
40009
40010     }
40011     
40012     
40013     if(config.toolbar){
40014         var tool_el = this.wrapper.createChild();    
40015         this.toolbar = Roo.factory(config.toolbar);
40016         var ti = [];
40017         if (config.toolbar.items) {
40018             ti = config.toolbar.items ;
40019             delete config.toolbar.items ;
40020         }
40021         
40022         var nitems = [];
40023         this.toolbar.render(tool_el);
40024         for(var i =0;i < ti.length;i++) {
40025           //  Roo.log(['add child', items[i]]);
40026             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40027         }
40028         this.toolbar.items = nitems;
40029         
40030         delete config.toolbar;
40031     }
40032     
40033     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40034     config.grid.scrollBody = true;;
40035     config.grid.monitorWindowResize = false; // turn off autosizing
40036     config.grid.autoHeight = false;
40037     config.grid.autoWidth = false;
40038     
40039     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40040     
40041     if (config.background) {
40042         // render grid on panel activation (if panel background)
40043         this.on('activate', function(gp) {
40044             if (!gp.grid.rendered) {
40045                 gp.grid.render(this.wrapper);
40046                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40047             }
40048         });
40049             
40050     } else {
40051         this.grid.render(this.wrapper);
40052         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40053
40054     }
40055     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40056     // ??? needed ??? config.el = this.wrapper;
40057     
40058     
40059     
40060   
40061     // xtype created footer. - not sure if will work as we normally have to render first..
40062     if (this.footer && !this.footer.el && this.footer.xtype) {
40063         
40064         var ctr = this.grid.getView().getFooterPanel(true);
40065         this.footer.dataSource = this.grid.dataSource;
40066         this.footer = Roo.factory(this.footer, Roo);
40067         this.footer.render(ctr);
40068         
40069     }
40070     
40071     
40072     
40073     
40074      
40075 };
40076
40077 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40078     getId : function(){
40079         return this.grid.id;
40080     },
40081     
40082     /**
40083      * Returns the grid for this panel
40084      * @return {Roo.bootstrap.Table} 
40085      */
40086     getGrid : function(){
40087         return this.grid;    
40088     },
40089     
40090     setSize : function(width, height){
40091         if(!this.ignoreResize(width, height)){
40092             var grid = this.grid;
40093             var size = this.adjustForComponents(width, height);
40094             // tfoot is not a footer?
40095           
40096             
40097             var gridel = grid.getGridEl();
40098             gridel.setSize(size.width, size.height);
40099             
40100             var tbd = grid.getGridEl().select('tbody', true).first();
40101             var thd = grid.getGridEl().select('thead',true).first();
40102             var tbf= grid.getGridEl().select('tfoot', true).first();
40103
40104             if (tbf) {
40105                 size.height -= thd.getHeight();
40106             }
40107             if (thd) {
40108                 size.height -= thd.getHeight();
40109             }
40110             
40111             tbd.setSize(size.width, size.height );
40112             // this is for the account management tab -seems to work there.
40113             var thd = grid.getGridEl().select('thead',true).first();
40114             //if (tbd) {
40115             //    tbd.setSize(size.width, size.height - thd.getHeight());
40116             //}
40117              
40118             grid.autoSize();
40119         }
40120     },
40121      
40122     
40123     
40124     beforeSlide : function(){
40125         this.grid.getView().scroller.clip();
40126     },
40127     
40128     afterSlide : function(){
40129         this.grid.getView().scroller.unclip();
40130     },
40131     
40132     destroy : function(){
40133         this.grid.destroy();
40134         delete this.grid;
40135         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40136     }
40137 });
40138
40139 /**
40140  * @class Roo.bootstrap.panel.Nest
40141  * @extends Roo.bootstrap.panel.Content
40142  * @constructor
40143  * Create a new Panel, that can contain a layout.Border.
40144  * 
40145  * 
40146  * @param {Roo.BorderLayout} layout The layout for this panel
40147  * @param {String/Object} config A string to set only the title or a config object
40148  */
40149 Roo.bootstrap.panel.Nest = function(config)
40150 {
40151     // construct with only one argument..
40152     /* FIXME - implement nicer consturctors
40153     if (layout.layout) {
40154         config = layout;
40155         layout = config.layout;
40156         delete config.layout;
40157     }
40158     if (layout.xtype && !layout.getEl) {
40159         // then layout needs constructing..
40160         layout = Roo.factory(layout, Roo);
40161     }
40162     */
40163     
40164     config.el =  config.layout.getEl();
40165     
40166     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40167     
40168     config.layout.monitorWindowResize = false; // turn off autosizing
40169     this.layout = config.layout;
40170     this.layout.getEl().addClass("roo-layout-nested-layout");
40171     this.layout.parent = this;
40172     
40173     
40174     
40175     
40176 };
40177
40178 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40179
40180     setSize : function(width, height){
40181         if(!this.ignoreResize(width, height)){
40182             var size = this.adjustForComponents(width, height);
40183             var el = this.layout.getEl();
40184             if (size.height < 1) {
40185                 el.setWidth(size.width);   
40186             } else {
40187                 el.setSize(size.width, size.height);
40188             }
40189             var touch = el.dom.offsetWidth;
40190             this.layout.layout();
40191             // ie requires a double layout on the first pass
40192             if(Roo.isIE && !this.initialized){
40193                 this.initialized = true;
40194                 this.layout.layout();
40195             }
40196         }
40197     },
40198     
40199     // activate all subpanels if not currently active..
40200     
40201     setActiveState : function(active){
40202         this.active = active;
40203         this.setActiveClass(active);
40204         
40205         if(!active){
40206             this.fireEvent("deactivate", this);
40207             return;
40208         }
40209         
40210         this.fireEvent("activate", this);
40211         // not sure if this should happen before or after..
40212         if (!this.layout) {
40213             return; // should not happen..
40214         }
40215         var reg = false;
40216         for (var r in this.layout.regions) {
40217             reg = this.layout.getRegion(r);
40218             if (reg.getActivePanel()) {
40219                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40220                 reg.setActivePanel(reg.getActivePanel());
40221                 continue;
40222             }
40223             if (!reg.panels.length) {
40224                 continue;
40225             }
40226             reg.showPanel(reg.getPanel(0));
40227         }
40228         
40229         
40230         
40231         
40232     },
40233     
40234     /**
40235      * Returns the nested BorderLayout for this panel
40236      * @return {Roo.BorderLayout} 
40237      */
40238     getLayout : function(){
40239         return this.layout;
40240     },
40241     
40242      /**
40243      * Adds a xtype elements to the layout of the nested panel
40244      * <pre><code>
40245
40246 panel.addxtype({
40247        xtype : 'ContentPanel',
40248        region: 'west',
40249        items: [ .... ]
40250    }
40251 );
40252
40253 panel.addxtype({
40254         xtype : 'NestedLayoutPanel',
40255         region: 'west',
40256         layout: {
40257            center: { },
40258            west: { }   
40259         },
40260         items : [ ... list of content panels or nested layout panels.. ]
40261    }
40262 );
40263 </code></pre>
40264      * @param {Object} cfg Xtype definition of item to add.
40265      */
40266     addxtype : function(cfg) {
40267         return this.layout.addxtype(cfg);
40268     
40269     }
40270 });/*
40271  * Based on:
40272  * Ext JS Library 1.1.1
40273  * Copyright(c) 2006-2007, Ext JS, LLC.
40274  *
40275  * Originally Released Under LGPL - original licence link has changed is not relivant.
40276  *
40277  * Fork - LGPL
40278  * <script type="text/javascript">
40279  */
40280 /**
40281  * @class Roo.TabPanel
40282  * @extends Roo.util.Observable
40283  * A lightweight tab container.
40284  * <br><br>
40285  * Usage:
40286  * <pre><code>
40287 // basic tabs 1, built from existing content
40288 var tabs = new Roo.TabPanel("tabs1");
40289 tabs.addTab("script", "View Script");
40290 tabs.addTab("markup", "View Markup");
40291 tabs.activate("script");
40292
40293 // more advanced tabs, built from javascript
40294 var jtabs = new Roo.TabPanel("jtabs");
40295 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40296
40297 // set up the UpdateManager
40298 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40299 var updater = tab2.getUpdateManager();
40300 updater.setDefaultUrl("ajax1.htm");
40301 tab2.on('activate', updater.refresh, updater, true);
40302
40303 // Use setUrl for Ajax loading
40304 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40305 tab3.setUrl("ajax2.htm", null, true);
40306
40307 // Disabled tab
40308 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40309 tab4.disable();
40310
40311 jtabs.activate("jtabs-1");
40312  * </code></pre>
40313  * @constructor
40314  * Create a new TabPanel.
40315  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40316  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40317  */
40318 Roo.bootstrap.panel.Tabs = function(config){
40319     /**
40320     * The container element for this TabPanel.
40321     * @type Roo.Element
40322     */
40323     this.el = Roo.get(config.el);
40324     delete config.el;
40325     if(config){
40326         if(typeof config == "boolean"){
40327             this.tabPosition = config ? "bottom" : "top";
40328         }else{
40329             Roo.apply(this, config);
40330         }
40331     }
40332     
40333     if(this.tabPosition == "bottom"){
40334         // if tabs are at the bottom = create the body first.
40335         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40336         this.el.addClass("roo-tabs-bottom");
40337     }
40338     // next create the tabs holders
40339     
40340     if (this.tabPosition == "west"){
40341         
40342         var reg = this.region; // fake it..
40343         while (reg) {
40344             if (!reg.mgr.parent) {
40345                 break;
40346             }
40347             reg = reg.mgr.parent.region;
40348         }
40349         Roo.log("got nest?");
40350         Roo.log(reg);
40351         if (reg.mgr.getRegion('west')) {
40352             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40353             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40354             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40355             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40356             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40357         
40358             
40359         }
40360         
40361         
40362     } else {
40363      
40364         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40365         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40366         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40367         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40368     }
40369     
40370     
40371     if(Roo.isIE){
40372         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40373     }
40374     
40375     // finally - if tabs are at the top, then create the body last..
40376     if(this.tabPosition != "bottom"){
40377         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40378          * @type Roo.Element
40379          */
40380         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40381         this.el.addClass("roo-tabs-top");
40382     }
40383     this.items = [];
40384
40385     this.bodyEl.setStyle("position", "relative");
40386
40387     this.active = null;
40388     this.activateDelegate = this.activate.createDelegate(this);
40389
40390     this.addEvents({
40391         /**
40392          * @event tabchange
40393          * Fires when the active tab changes
40394          * @param {Roo.TabPanel} this
40395          * @param {Roo.TabPanelItem} activePanel The new active tab
40396          */
40397         "tabchange": true,
40398         /**
40399          * @event beforetabchange
40400          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40401          * @param {Roo.TabPanel} this
40402          * @param {Object} e Set cancel to true on this object to cancel the tab change
40403          * @param {Roo.TabPanelItem} tab The tab being changed to
40404          */
40405         "beforetabchange" : true
40406     });
40407
40408     Roo.EventManager.onWindowResize(this.onResize, this);
40409     this.cpad = this.el.getPadding("lr");
40410     this.hiddenCount = 0;
40411
40412
40413     // toolbar on the tabbar support...
40414     if (this.toolbar) {
40415         alert("no toolbar support yet");
40416         this.toolbar  = false;
40417         /*
40418         var tcfg = this.toolbar;
40419         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40420         this.toolbar = new Roo.Toolbar(tcfg);
40421         if (Roo.isSafari) {
40422             var tbl = tcfg.container.child('table', true);
40423             tbl.setAttribute('width', '100%');
40424         }
40425         */
40426         
40427     }
40428    
40429
40430
40431     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40432 };
40433
40434 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40435     /*
40436      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40437      */
40438     tabPosition : "top",
40439     /*
40440      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40441      */
40442     currentTabWidth : 0,
40443     /*
40444      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40445      */
40446     minTabWidth : 40,
40447     /*
40448      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40449      */
40450     maxTabWidth : 250,
40451     /*
40452      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40453      */
40454     preferredTabWidth : 175,
40455     /*
40456      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40457      */
40458     resizeTabs : false,
40459     /*
40460      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40461      */
40462     monitorResize : true,
40463     /*
40464      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40465      */
40466     toolbar : false,  // set by caller..
40467     
40468     region : false, /// set by caller
40469     
40470     disableTooltips : true, // not used yet...
40471
40472     /**
40473      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40474      * @param {String} id The id of the div to use <b>or create</b>
40475      * @param {String} text The text for the tab
40476      * @param {String} content (optional) Content to put in the TabPanelItem body
40477      * @param {Boolean} closable (optional) True to create a close icon on the tab
40478      * @return {Roo.TabPanelItem} The created TabPanelItem
40479      */
40480     addTab : function(id, text, content, closable, tpl)
40481     {
40482         var item = new Roo.bootstrap.panel.TabItem({
40483             panel: this,
40484             id : id,
40485             text : text,
40486             closable : closable,
40487             tpl : tpl
40488         });
40489         this.addTabItem(item);
40490         if(content){
40491             item.setContent(content);
40492         }
40493         return item;
40494     },
40495
40496     /**
40497      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40498      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40499      * @return {Roo.TabPanelItem}
40500      */
40501     getTab : function(id){
40502         return this.items[id];
40503     },
40504
40505     /**
40506      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40507      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40508      */
40509     hideTab : function(id){
40510         var t = this.items[id];
40511         if(!t.isHidden()){
40512            t.setHidden(true);
40513            this.hiddenCount++;
40514            this.autoSizeTabs();
40515         }
40516     },
40517
40518     /**
40519      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40520      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40521      */
40522     unhideTab : function(id){
40523         var t = this.items[id];
40524         if(t.isHidden()){
40525            t.setHidden(false);
40526            this.hiddenCount--;
40527            this.autoSizeTabs();
40528         }
40529     },
40530
40531     /**
40532      * Adds an existing {@link Roo.TabPanelItem}.
40533      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40534      */
40535     addTabItem : function(item)
40536     {
40537         this.items[item.id] = item;
40538         this.items.push(item);
40539         this.autoSizeTabs();
40540       //  if(this.resizeTabs){
40541     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40542   //         this.autoSizeTabs();
40543 //        }else{
40544 //            item.autoSize();
40545        // }
40546     },
40547
40548     /**
40549      * Removes a {@link Roo.TabPanelItem}.
40550      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40551      */
40552     removeTab : function(id){
40553         var items = this.items;
40554         var tab = items[id];
40555         if(!tab) { return; }
40556         var index = items.indexOf(tab);
40557         if(this.active == tab && items.length > 1){
40558             var newTab = this.getNextAvailable(index);
40559             if(newTab) {
40560                 newTab.activate();
40561             }
40562         }
40563         this.stripEl.dom.removeChild(tab.pnode.dom);
40564         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40565             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40566         }
40567         items.splice(index, 1);
40568         delete this.items[tab.id];
40569         tab.fireEvent("close", tab);
40570         tab.purgeListeners();
40571         this.autoSizeTabs();
40572     },
40573
40574     getNextAvailable : function(start){
40575         var items = this.items;
40576         var index = start;
40577         // look for a next tab that will slide over to
40578         // replace the one being removed
40579         while(index < items.length){
40580             var item = items[++index];
40581             if(item && !item.isHidden()){
40582                 return item;
40583             }
40584         }
40585         // if one isn't found select the previous tab (on the left)
40586         index = start;
40587         while(index >= 0){
40588             var item = items[--index];
40589             if(item && !item.isHidden()){
40590                 return item;
40591             }
40592         }
40593         return null;
40594     },
40595
40596     /**
40597      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40598      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40599      */
40600     disableTab : function(id){
40601         var tab = this.items[id];
40602         if(tab && this.active != tab){
40603             tab.disable();
40604         }
40605     },
40606
40607     /**
40608      * Enables a {@link Roo.TabPanelItem} that is disabled.
40609      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40610      */
40611     enableTab : function(id){
40612         var tab = this.items[id];
40613         tab.enable();
40614     },
40615
40616     /**
40617      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40618      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40619      * @return {Roo.TabPanelItem} The TabPanelItem.
40620      */
40621     activate : function(id)
40622     {
40623         //Roo.log('activite:'  + id);
40624         
40625         var tab = this.items[id];
40626         if(!tab){
40627             return null;
40628         }
40629         if(tab == this.active || tab.disabled){
40630             return tab;
40631         }
40632         var e = {};
40633         this.fireEvent("beforetabchange", this, e, tab);
40634         if(e.cancel !== true && !tab.disabled){
40635             if(this.active){
40636                 this.active.hide();
40637             }
40638             this.active = this.items[id];
40639             this.active.show();
40640             this.fireEvent("tabchange", this, this.active);
40641         }
40642         return tab;
40643     },
40644
40645     /**
40646      * Gets the active {@link Roo.TabPanelItem}.
40647      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40648      */
40649     getActiveTab : function(){
40650         return this.active;
40651     },
40652
40653     /**
40654      * Updates the tab body element to fit the height of the container element
40655      * for overflow scrolling
40656      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40657      */
40658     syncHeight : function(targetHeight){
40659         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40660         var bm = this.bodyEl.getMargins();
40661         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40662         this.bodyEl.setHeight(newHeight);
40663         return newHeight;
40664     },
40665
40666     onResize : function(){
40667         if(this.monitorResize){
40668             this.autoSizeTabs();
40669         }
40670     },
40671
40672     /**
40673      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40674      */
40675     beginUpdate : function(){
40676         this.updating = true;
40677     },
40678
40679     /**
40680      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40681      */
40682     endUpdate : function(){
40683         this.updating = false;
40684         this.autoSizeTabs();
40685     },
40686
40687     /**
40688      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40689      */
40690     autoSizeTabs : function()
40691     {
40692         var count = this.items.length;
40693         var vcount = count - this.hiddenCount;
40694         
40695         if (vcount < 2) {
40696             this.stripEl.hide();
40697         } else {
40698             this.stripEl.show();
40699         }
40700         
40701         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40702             return;
40703         }
40704         
40705         
40706         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40707         var availWidth = Math.floor(w / vcount);
40708         var b = this.stripBody;
40709         if(b.getWidth() > w){
40710             var tabs = this.items;
40711             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40712             if(availWidth < this.minTabWidth){
40713                 /*if(!this.sleft){    // incomplete scrolling code
40714                     this.createScrollButtons();
40715                 }
40716                 this.showScroll();
40717                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40718             }
40719         }else{
40720             if(this.currentTabWidth < this.preferredTabWidth){
40721                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40722             }
40723         }
40724     },
40725
40726     /**
40727      * Returns the number of tabs in this TabPanel.
40728      * @return {Number}
40729      */
40730      getCount : function(){
40731          return this.items.length;
40732      },
40733
40734     /**
40735      * Resizes all the tabs to the passed width
40736      * @param {Number} The new width
40737      */
40738     setTabWidth : function(width){
40739         this.currentTabWidth = width;
40740         for(var i = 0, len = this.items.length; i < len; i++) {
40741                 if(!this.items[i].isHidden()) {
40742                 this.items[i].setWidth(width);
40743             }
40744         }
40745     },
40746
40747     /**
40748      * Destroys this TabPanel
40749      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40750      */
40751     destroy : function(removeEl){
40752         Roo.EventManager.removeResizeListener(this.onResize, this);
40753         for(var i = 0, len = this.items.length; i < len; i++){
40754             this.items[i].purgeListeners();
40755         }
40756         if(removeEl === true){
40757             this.el.update("");
40758             this.el.remove();
40759         }
40760     },
40761     
40762     createStrip : function(container)
40763     {
40764         var strip = document.createElement("nav");
40765         strip.className = Roo.bootstrap.version == 4 ?
40766             "navbar-light bg-light" : 
40767             "navbar navbar-default"; //"x-tabs-wrap";
40768         container.appendChild(strip);
40769         return strip;
40770     },
40771     
40772     createStripList : function(strip)
40773     {
40774         // div wrapper for retard IE
40775         // returns the "tr" element.
40776         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40777         //'<div class="x-tabs-strip-wrap">'+
40778           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40779           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40780         return strip.firstChild; //.firstChild.firstChild.firstChild;
40781     },
40782     createBody : function(container)
40783     {
40784         var body = document.createElement("div");
40785         Roo.id(body, "tab-body");
40786         //Roo.fly(body).addClass("x-tabs-body");
40787         Roo.fly(body).addClass("tab-content");
40788         container.appendChild(body);
40789         return body;
40790     },
40791     createItemBody :function(bodyEl, id){
40792         var body = Roo.getDom(id);
40793         if(!body){
40794             body = document.createElement("div");
40795             body.id = id;
40796         }
40797         //Roo.fly(body).addClass("x-tabs-item-body");
40798         Roo.fly(body).addClass("tab-pane");
40799          bodyEl.insertBefore(body, bodyEl.firstChild);
40800         return body;
40801     },
40802     /** @private */
40803     createStripElements :  function(stripEl, text, closable, tpl)
40804     {
40805         var td = document.createElement("li"); // was td..
40806         td.className = 'nav-item';
40807         
40808         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40809         
40810         
40811         stripEl.appendChild(td);
40812         /*if(closable){
40813             td.className = "x-tabs-closable";
40814             if(!this.closeTpl){
40815                 this.closeTpl = new Roo.Template(
40816                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40817                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40818                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40819                 );
40820             }
40821             var el = this.closeTpl.overwrite(td, {"text": text});
40822             var close = el.getElementsByTagName("div")[0];
40823             var inner = el.getElementsByTagName("em")[0];
40824             return {"el": el, "close": close, "inner": inner};
40825         } else {
40826         */
40827         // not sure what this is..
40828 //            if(!this.tabTpl){
40829                 //this.tabTpl = new Roo.Template(
40830                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40831                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40832                 //);
40833 //                this.tabTpl = new Roo.Template(
40834 //                   '<a href="#">' +
40835 //                   '<span unselectable="on"' +
40836 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40837 //                            ' >{text}</span></a>'
40838 //                );
40839 //                
40840 //            }
40841
40842
40843             var template = tpl || this.tabTpl || false;
40844             
40845             if(!template){
40846                 template =  new Roo.Template(
40847                         Roo.bootstrap.version == 4 ? 
40848                             (
40849                                 '<a class="nav-link" href="#" unselectable="on"' +
40850                                      (this.disableTooltips ? '' : ' title="{text}"') +
40851                                      ' >{text}</a>'
40852                             ) : (
40853                                 '<a class="nav-link" href="#">' +
40854                                 '<span unselectable="on"' +
40855                                          (this.disableTooltips ? '' : ' title="{text}"') +
40856                                     ' >{text}</span></a>'
40857                             )
40858                 );
40859             }
40860             
40861             switch (typeof(template)) {
40862                 case 'object' :
40863                     break;
40864                 case 'string' :
40865                     template = new Roo.Template(template);
40866                     break;
40867                 default :
40868                     break;
40869             }
40870             
40871             var el = template.overwrite(td, {"text": text});
40872             
40873             var inner = el.getElementsByTagName("span")[0];
40874             
40875             return {"el": el, "inner": inner};
40876             
40877     }
40878         
40879     
40880 });
40881
40882 /**
40883  * @class Roo.TabPanelItem
40884  * @extends Roo.util.Observable
40885  * Represents an individual item (tab plus body) in a TabPanel.
40886  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40887  * @param {String} id The id of this TabPanelItem
40888  * @param {String} text The text for the tab of this TabPanelItem
40889  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40890  */
40891 Roo.bootstrap.panel.TabItem = function(config){
40892     /**
40893      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40894      * @type Roo.TabPanel
40895      */
40896     this.tabPanel = config.panel;
40897     /**
40898      * The id for this TabPanelItem
40899      * @type String
40900      */
40901     this.id = config.id;
40902     /** @private */
40903     this.disabled = false;
40904     /** @private */
40905     this.text = config.text;
40906     /** @private */
40907     this.loaded = false;
40908     this.closable = config.closable;
40909
40910     /**
40911      * The body element for this TabPanelItem.
40912      * @type Roo.Element
40913      */
40914     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40915     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40916     this.bodyEl.setStyle("display", "block");
40917     this.bodyEl.setStyle("zoom", "1");
40918     //this.hideAction();
40919
40920     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40921     /** @private */
40922     this.el = Roo.get(els.el);
40923     this.inner = Roo.get(els.inner, true);
40924      this.textEl = Roo.bootstrap.version == 4 ?
40925         this.el : Roo.get(this.el.dom.firstChild, true);
40926
40927     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40928     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40929
40930     
40931 //    this.el.on("mousedown", this.onTabMouseDown, this);
40932     this.el.on("click", this.onTabClick, this);
40933     /** @private */
40934     if(config.closable){
40935         var c = Roo.get(els.close, true);
40936         c.dom.title = this.closeText;
40937         c.addClassOnOver("close-over");
40938         c.on("click", this.closeClick, this);
40939      }
40940
40941     this.addEvents({
40942          /**
40943          * @event activate
40944          * Fires when this tab becomes the active tab.
40945          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40946          * @param {Roo.TabPanelItem} this
40947          */
40948         "activate": true,
40949         /**
40950          * @event beforeclose
40951          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40952          * @param {Roo.TabPanelItem} this
40953          * @param {Object} e Set cancel to true on this object to cancel the close.
40954          */
40955         "beforeclose": true,
40956         /**
40957          * @event close
40958          * Fires when this tab is closed.
40959          * @param {Roo.TabPanelItem} this
40960          */
40961          "close": true,
40962         /**
40963          * @event deactivate
40964          * Fires when this tab is no longer the active tab.
40965          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40966          * @param {Roo.TabPanelItem} this
40967          */
40968          "deactivate" : true
40969     });
40970     this.hidden = false;
40971
40972     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40973 };
40974
40975 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40976            {
40977     purgeListeners : function(){
40978        Roo.util.Observable.prototype.purgeListeners.call(this);
40979        this.el.removeAllListeners();
40980     },
40981     /**
40982      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40983      */
40984     show : function(){
40985         this.status_node.addClass("active");
40986         this.showAction();
40987         if(Roo.isOpera){
40988             this.tabPanel.stripWrap.repaint();
40989         }
40990         this.fireEvent("activate", this.tabPanel, this);
40991     },
40992
40993     /**
40994      * Returns true if this tab is the active tab.
40995      * @return {Boolean}
40996      */
40997     isActive : function(){
40998         return this.tabPanel.getActiveTab() == this;
40999     },
41000
41001     /**
41002      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41003      */
41004     hide : function(){
41005         this.status_node.removeClass("active");
41006         this.hideAction();
41007         this.fireEvent("deactivate", this.tabPanel, this);
41008     },
41009
41010     hideAction : function(){
41011         this.bodyEl.hide();
41012         this.bodyEl.setStyle("position", "absolute");
41013         this.bodyEl.setLeft("-20000px");
41014         this.bodyEl.setTop("-20000px");
41015     },
41016
41017     showAction : function(){
41018         this.bodyEl.setStyle("position", "relative");
41019         this.bodyEl.setTop("");
41020         this.bodyEl.setLeft("");
41021         this.bodyEl.show();
41022     },
41023
41024     /**
41025      * Set the tooltip for the tab.
41026      * @param {String} tooltip The tab's tooltip
41027      */
41028     setTooltip : function(text){
41029         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41030             this.textEl.dom.qtip = text;
41031             this.textEl.dom.removeAttribute('title');
41032         }else{
41033             this.textEl.dom.title = text;
41034         }
41035     },
41036
41037     onTabClick : function(e){
41038         e.preventDefault();
41039         this.tabPanel.activate(this.id);
41040     },
41041
41042     onTabMouseDown : function(e){
41043         e.preventDefault();
41044         this.tabPanel.activate(this.id);
41045     },
41046 /*
41047     getWidth : function(){
41048         return this.inner.getWidth();
41049     },
41050
41051     setWidth : function(width){
41052         var iwidth = width - this.linode.getPadding("lr");
41053         this.inner.setWidth(iwidth);
41054         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41055         this.linode.setWidth(width);
41056     },
41057 */
41058     /**
41059      * Show or hide the tab
41060      * @param {Boolean} hidden True to hide or false to show.
41061      */
41062     setHidden : function(hidden){
41063         this.hidden = hidden;
41064         this.linode.setStyle("display", hidden ? "none" : "");
41065     },
41066
41067     /**
41068      * Returns true if this tab is "hidden"
41069      * @return {Boolean}
41070      */
41071     isHidden : function(){
41072         return this.hidden;
41073     },
41074
41075     /**
41076      * Returns the text for this tab
41077      * @return {String}
41078      */
41079     getText : function(){
41080         return this.text;
41081     },
41082     /*
41083     autoSize : function(){
41084         //this.el.beginMeasure();
41085         this.textEl.setWidth(1);
41086         /*
41087          *  #2804 [new] Tabs in Roojs
41088          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41089          */
41090         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41091         //this.el.endMeasure();
41092     //},
41093
41094     /**
41095      * Sets the text for the tab (Note: this also sets the tooltip text)
41096      * @param {String} text The tab's text and tooltip
41097      */
41098     setText : function(text){
41099         this.text = text;
41100         this.textEl.update(text);
41101         this.setTooltip(text);
41102         //if(!this.tabPanel.resizeTabs){
41103         //    this.autoSize();
41104         //}
41105     },
41106     /**
41107      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41108      */
41109     activate : function(){
41110         this.tabPanel.activate(this.id);
41111     },
41112
41113     /**
41114      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41115      */
41116     disable : function(){
41117         if(this.tabPanel.active != this){
41118             this.disabled = true;
41119             this.status_node.addClass("disabled");
41120         }
41121     },
41122
41123     /**
41124      * Enables this TabPanelItem if it was previously disabled.
41125      */
41126     enable : function(){
41127         this.disabled = false;
41128         this.status_node.removeClass("disabled");
41129     },
41130
41131     /**
41132      * Sets the content for this TabPanelItem.
41133      * @param {String} content The content
41134      * @param {Boolean} loadScripts true to look for and load scripts
41135      */
41136     setContent : function(content, loadScripts){
41137         this.bodyEl.update(content, loadScripts);
41138     },
41139
41140     /**
41141      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41142      * @return {Roo.UpdateManager} The UpdateManager
41143      */
41144     getUpdateManager : function(){
41145         return this.bodyEl.getUpdateManager();
41146     },
41147
41148     /**
41149      * Set a URL to be used to load the content for this TabPanelItem.
41150      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41151      * @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)
41152      * @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)
41153      * @return {Roo.UpdateManager} The UpdateManager
41154      */
41155     setUrl : function(url, params, loadOnce){
41156         if(this.refreshDelegate){
41157             this.un('activate', this.refreshDelegate);
41158         }
41159         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41160         this.on("activate", this.refreshDelegate);
41161         return this.bodyEl.getUpdateManager();
41162     },
41163
41164     /** @private */
41165     _handleRefresh : function(url, params, loadOnce){
41166         if(!loadOnce || !this.loaded){
41167             var updater = this.bodyEl.getUpdateManager();
41168             updater.update(url, params, this._setLoaded.createDelegate(this));
41169         }
41170     },
41171
41172     /**
41173      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41174      *   Will fail silently if the setUrl method has not been called.
41175      *   This does not activate the panel, just updates its content.
41176      */
41177     refresh : function(){
41178         if(this.refreshDelegate){
41179            this.loaded = false;
41180            this.refreshDelegate();
41181         }
41182     },
41183
41184     /** @private */
41185     _setLoaded : function(){
41186         this.loaded = true;
41187     },
41188
41189     /** @private */
41190     closeClick : function(e){
41191         var o = {};
41192         e.stopEvent();
41193         this.fireEvent("beforeclose", this, o);
41194         if(o.cancel !== true){
41195             this.tabPanel.removeTab(this.id);
41196         }
41197     },
41198     /**
41199      * The text displayed in the tooltip for the close icon.
41200      * @type String
41201      */
41202     closeText : "Close this tab"
41203 });
41204 /**
41205 *    This script refer to:
41206 *    Title: International Telephone Input
41207 *    Author: Jack O'Connor
41208 *    Code version:  v12.1.12
41209 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41210 **/
41211
41212 Roo.bootstrap.PhoneInputData = function() {
41213     var d = [
41214       [
41215         "Afghanistan (‫افغانستان‬‎)",
41216         "af",
41217         "93"
41218       ],
41219       [
41220         "Albania (Shqipëri)",
41221         "al",
41222         "355"
41223       ],
41224       [
41225         "Algeria (‫الجزائر‬‎)",
41226         "dz",
41227         "213"
41228       ],
41229       [
41230         "American Samoa",
41231         "as",
41232         "1684"
41233       ],
41234       [
41235         "Andorra",
41236         "ad",
41237         "376"
41238       ],
41239       [
41240         "Angola",
41241         "ao",
41242         "244"
41243       ],
41244       [
41245         "Anguilla",
41246         "ai",
41247         "1264"
41248       ],
41249       [
41250         "Antigua and Barbuda",
41251         "ag",
41252         "1268"
41253       ],
41254       [
41255         "Argentina",
41256         "ar",
41257         "54"
41258       ],
41259       [
41260         "Armenia (Հայաստան)",
41261         "am",
41262         "374"
41263       ],
41264       [
41265         "Aruba",
41266         "aw",
41267         "297"
41268       ],
41269       [
41270         "Australia",
41271         "au",
41272         "61",
41273         0
41274       ],
41275       [
41276         "Austria (Österreich)",
41277         "at",
41278         "43"
41279       ],
41280       [
41281         "Azerbaijan (Azərbaycan)",
41282         "az",
41283         "994"
41284       ],
41285       [
41286         "Bahamas",
41287         "bs",
41288         "1242"
41289       ],
41290       [
41291         "Bahrain (‫البحرين‬‎)",
41292         "bh",
41293         "973"
41294       ],
41295       [
41296         "Bangladesh (বাংলাদেশ)",
41297         "bd",
41298         "880"
41299       ],
41300       [
41301         "Barbados",
41302         "bb",
41303         "1246"
41304       ],
41305       [
41306         "Belarus (Беларусь)",
41307         "by",
41308         "375"
41309       ],
41310       [
41311         "Belgium (België)",
41312         "be",
41313         "32"
41314       ],
41315       [
41316         "Belize",
41317         "bz",
41318         "501"
41319       ],
41320       [
41321         "Benin (Bénin)",
41322         "bj",
41323         "229"
41324       ],
41325       [
41326         "Bermuda",
41327         "bm",
41328         "1441"
41329       ],
41330       [
41331         "Bhutan (འབྲུག)",
41332         "bt",
41333         "975"
41334       ],
41335       [
41336         "Bolivia",
41337         "bo",
41338         "591"
41339       ],
41340       [
41341         "Bosnia and Herzegovina (Босна и Херцеговина)",
41342         "ba",
41343         "387"
41344       ],
41345       [
41346         "Botswana",
41347         "bw",
41348         "267"
41349       ],
41350       [
41351         "Brazil (Brasil)",
41352         "br",
41353         "55"
41354       ],
41355       [
41356         "British Indian Ocean Territory",
41357         "io",
41358         "246"
41359       ],
41360       [
41361         "British Virgin Islands",
41362         "vg",
41363         "1284"
41364       ],
41365       [
41366         "Brunei",
41367         "bn",
41368         "673"
41369       ],
41370       [
41371         "Bulgaria (България)",
41372         "bg",
41373         "359"
41374       ],
41375       [
41376         "Burkina Faso",
41377         "bf",
41378         "226"
41379       ],
41380       [
41381         "Burundi (Uburundi)",
41382         "bi",
41383         "257"
41384       ],
41385       [
41386         "Cambodia (កម្ពុជា)",
41387         "kh",
41388         "855"
41389       ],
41390       [
41391         "Cameroon (Cameroun)",
41392         "cm",
41393         "237"
41394       ],
41395       [
41396         "Canada",
41397         "ca",
41398         "1",
41399         1,
41400         ["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"]
41401       ],
41402       [
41403         "Cape Verde (Kabu Verdi)",
41404         "cv",
41405         "238"
41406       ],
41407       [
41408         "Caribbean Netherlands",
41409         "bq",
41410         "599",
41411         1
41412       ],
41413       [
41414         "Cayman Islands",
41415         "ky",
41416         "1345"
41417       ],
41418       [
41419         "Central African Republic (République centrafricaine)",
41420         "cf",
41421         "236"
41422       ],
41423       [
41424         "Chad (Tchad)",
41425         "td",
41426         "235"
41427       ],
41428       [
41429         "Chile",
41430         "cl",
41431         "56"
41432       ],
41433       [
41434         "China (中国)",
41435         "cn",
41436         "86"
41437       ],
41438       [
41439         "Christmas Island",
41440         "cx",
41441         "61",
41442         2
41443       ],
41444       [
41445         "Cocos (Keeling) Islands",
41446         "cc",
41447         "61",
41448         1
41449       ],
41450       [
41451         "Colombia",
41452         "co",
41453         "57"
41454       ],
41455       [
41456         "Comoros (‫جزر القمر‬‎)",
41457         "km",
41458         "269"
41459       ],
41460       [
41461         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41462         "cd",
41463         "243"
41464       ],
41465       [
41466         "Congo (Republic) (Congo-Brazzaville)",
41467         "cg",
41468         "242"
41469       ],
41470       [
41471         "Cook Islands",
41472         "ck",
41473         "682"
41474       ],
41475       [
41476         "Costa Rica",
41477         "cr",
41478         "506"
41479       ],
41480       [
41481         "Côte d’Ivoire",
41482         "ci",
41483         "225"
41484       ],
41485       [
41486         "Croatia (Hrvatska)",
41487         "hr",
41488         "385"
41489       ],
41490       [
41491         "Cuba",
41492         "cu",
41493         "53"
41494       ],
41495       [
41496         "Curaçao",
41497         "cw",
41498         "599",
41499         0
41500       ],
41501       [
41502         "Cyprus (Κύπρος)",
41503         "cy",
41504         "357"
41505       ],
41506       [
41507         "Czech Republic (Česká republika)",
41508         "cz",
41509         "420"
41510       ],
41511       [
41512         "Denmark (Danmark)",
41513         "dk",
41514         "45"
41515       ],
41516       [
41517         "Djibouti",
41518         "dj",
41519         "253"
41520       ],
41521       [
41522         "Dominica",
41523         "dm",
41524         "1767"
41525       ],
41526       [
41527         "Dominican Republic (República Dominicana)",
41528         "do",
41529         "1",
41530         2,
41531         ["809", "829", "849"]
41532       ],
41533       [
41534         "Ecuador",
41535         "ec",
41536         "593"
41537       ],
41538       [
41539         "Egypt (‫مصر‬‎)",
41540         "eg",
41541         "20"
41542       ],
41543       [
41544         "El Salvador",
41545         "sv",
41546         "503"
41547       ],
41548       [
41549         "Equatorial Guinea (Guinea Ecuatorial)",
41550         "gq",
41551         "240"
41552       ],
41553       [
41554         "Eritrea",
41555         "er",
41556         "291"
41557       ],
41558       [
41559         "Estonia (Eesti)",
41560         "ee",
41561         "372"
41562       ],
41563       [
41564         "Ethiopia",
41565         "et",
41566         "251"
41567       ],
41568       [
41569         "Falkland Islands (Islas Malvinas)",
41570         "fk",
41571         "500"
41572       ],
41573       [
41574         "Faroe Islands (Føroyar)",
41575         "fo",
41576         "298"
41577       ],
41578       [
41579         "Fiji",
41580         "fj",
41581         "679"
41582       ],
41583       [
41584         "Finland (Suomi)",
41585         "fi",
41586         "358",
41587         0
41588       ],
41589       [
41590         "France",
41591         "fr",
41592         "33"
41593       ],
41594       [
41595         "French Guiana (Guyane française)",
41596         "gf",
41597         "594"
41598       ],
41599       [
41600         "French Polynesia (Polynésie française)",
41601         "pf",
41602         "689"
41603       ],
41604       [
41605         "Gabon",
41606         "ga",
41607         "241"
41608       ],
41609       [
41610         "Gambia",
41611         "gm",
41612         "220"
41613       ],
41614       [
41615         "Georgia (საქართველო)",
41616         "ge",
41617         "995"
41618       ],
41619       [
41620         "Germany (Deutschland)",
41621         "de",
41622         "49"
41623       ],
41624       [
41625         "Ghana (Gaana)",
41626         "gh",
41627         "233"
41628       ],
41629       [
41630         "Gibraltar",
41631         "gi",
41632         "350"
41633       ],
41634       [
41635         "Greece (Ελλάδα)",
41636         "gr",
41637         "30"
41638       ],
41639       [
41640         "Greenland (Kalaallit Nunaat)",
41641         "gl",
41642         "299"
41643       ],
41644       [
41645         "Grenada",
41646         "gd",
41647         "1473"
41648       ],
41649       [
41650         "Guadeloupe",
41651         "gp",
41652         "590",
41653         0
41654       ],
41655       [
41656         "Guam",
41657         "gu",
41658         "1671"
41659       ],
41660       [
41661         "Guatemala",
41662         "gt",
41663         "502"
41664       ],
41665       [
41666         "Guernsey",
41667         "gg",
41668         "44",
41669         1
41670       ],
41671       [
41672         "Guinea (Guinée)",
41673         "gn",
41674         "224"
41675       ],
41676       [
41677         "Guinea-Bissau (Guiné Bissau)",
41678         "gw",
41679         "245"
41680       ],
41681       [
41682         "Guyana",
41683         "gy",
41684         "592"
41685       ],
41686       [
41687         "Haiti",
41688         "ht",
41689         "509"
41690       ],
41691       [
41692         "Honduras",
41693         "hn",
41694         "504"
41695       ],
41696       [
41697         "Hong Kong (香港)",
41698         "hk",
41699         "852"
41700       ],
41701       [
41702         "Hungary (Magyarország)",
41703         "hu",
41704         "36"
41705       ],
41706       [
41707         "Iceland (Ísland)",
41708         "is",
41709         "354"
41710       ],
41711       [
41712         "India (भारत)",
41713         "in",
41714         "91"
41715       ],
41716       [
41717         "Indonesia",
41718         "id",
41719         "62"
41720       ],
41721       [
41722         "Iran (‫ایران‬‎)",
41723         "ir",
41724         "98"
41725       ],
41726       [
41727         "Iraq (‫العراق‬‎)",
41728         "iq",
41729         "964"
41730       ],
41731       [
41732         "Ireland",
41733         "ie",
41734         "353"
41735       ],
41736       [
41737         "Isle of Man",
41738         "im",
41739         "44",
41740         2
41741       ],
41742       [
41743         "Israel (‫ישראל‬‎)",
41744         "il",
41745         "972"
41746       ],
41747       [
41748         "Italy (Italia)",
41749         "it",
41750         "39",
41751         0
41752       ],
41753       [
41754         "Jamaica",
41755         "jm",
41756         "1876"
41757       ],
41758       [
41759         "Japan (日本)",
41760         "jp",
41761         "81"
41762       ],
41763       [
41764         "Jersey",
41765         "je",
41766         "44",
41767         3
41768       ],
41769       [
41770         "Jordan (‫الأردن‬‎)",
41771         "jo",
41772         "962"
41773       ],
41774       [
41775         "Kazakhstan (Казахстан)",
41776         "kz",
41777         "7",
41778         1
41779       ],
41780       [
41781         "Kenya",
41782         "ke",
41783         "254"
41784       ],
41785       [
41786         "Kiribati",
41787         "ki",
41788         "686"
41789       ],
41790       [
41791         "Kosovo",
41792         "xk",
41793         "383"
41794       ],
41795       [
41796         "Kuwait (‫الكويت‬‎)",
41797         "kw",
41798         "965"
41799       ],
41800       [
41801         "Kyrgyzstan (Кыргызстан)",
41802         "kg",
41803         "996"
41804       ],
41805       [
41806         "Laos (ລາວ)",
41807         "la",
41808         "856"
41809       ],
41810       [
41811         "Latvia (Latvija)",
41812         "lv",
41813         "371"
41814       ],
41815       [
41816         "Lebanon (‫لبنان‬‎)",
41817         "lb",
41818         "961"
41819       ],
41820       [
41821         "Lesotho",
41822         "ls",
41823         "266"
41824       ],
41825       [
41826         "Liberia",
41827         "lr",
41828         "231"
41829       ],
41830       [
41831         "Libya (‫ليبيا‬‎)",
41832         "ly",
41833         "218"
41834       ],
41835       [
41836         "Liechtenstein",
41837         "li",
41838         "423"
41839       ],
41840       [
41841         "Lithuania (Lietuva)",
41842         "lt",
41843         "370"
41844       ],
41845       [
41846         "Luxembourg",
41847         "lu",
41848         "352"
41849       ],
41850       [
41851         "Macau (澳門)",
41852         "mo",
41853         "853"
41854       ],
41855       [
41856         "Macedonia (FYROM) (Македонија)",
41857         "mk",
41858         "389"
41859       ],
41860       [
41861         "Madagascar (Madagasikara)",
41862         "mg",
41863         "261"
41864       ],
41865       [
41866         "Malawi",
41867         "mw",
41868         "265"
41869       ],
41870       [
41871         "Malaysia",
41872         "my",
41873         "60"
41874       ],
41875       [
41876         "Maldives",
41877         "mv",
41878         "960"
41879       ],
41880       [
41881         "Mali",
41882         "ml",
41883         "223"
41884       ],
41885       [
41886         "Malta",
41887         "mt",
41888         "356"
41889       ],
41890       [
41891         "Marshall Islands",
41892         "mh",
41893         "692"
41894       ],
41895       [
41896         "Martinique",
41897         "mq",
41898         "596"
41899       ],
41900       [
41901         "Mauritania (‫موريتانيا‬‎)",
41902         "mr",
41903         "222"
41904       ],
41905       [
41906         "Mauritius (Moris)",
41907         "mu",
41908         "230"
41909       ],
41910       [
41911         "Mayotte",
41912         "yt",
41913         "262",
41914         1
41915       ],
41916       [
41917         "Mexico (México)",
41918         "mx",
41919         "52"
41920       ],
41921       [
41922         "Micronesia",
41923         "fm",
41924         "691"
41925       ],
41926       [
41927         "Moldova (Republica Moldova)",
41928         "md",
41929         "373"
41930       ],
41931       [
41932         "Monaco",
41933         "mc",
41934         "377"
41935       ],
41936       [
41937         "Mongolia (Монгол)",
41938         "mn",
41939         "976"
41940       ],
41941       [
41942         "Montenegro (Crna Gora)",
41943         "me",
41944         "382"
41945       ],
41946       [
41947         "Montserrat",
41948         "ms",
41949         "1664"
41950       ],
41951       [
41952         "Morocco (‫المغرب‬‎)",
41953         "ma",
41954         "212",
41955         0
41956       ],
41957       [
41958         "Mozambique (Moçambique)",
41959         "mz",
41960         "258"
41961       ],
41962       [
41963         "Myanmar (Burma) (မြန်မာ)",
41964         "mm",
41965         "95"
41966       ],
41967       [
41968         "Namibia (Namibië)",
41969         "na",
41970         "264"
41971       ],
41972       [
41973         "Nauru",
41974         "nr",
41975         "674"
41976       ],
41977       [
41978         "Nepal (नेपाल)",
41979         "np",
41980         "977"
41981       ],
41982       [
41983         "Netherlands (Nederland)",
41984         "nl",
41985         "31"
41986       ],
41987       [
41988         "New Caledonia (Nouvelle-Calédonie)",
41989         "nc",
41990         "687"
41991       ],
41992       [
41993         "New Zealand",
41994         "nz",
41995         "64"
41996       ],
41997       [
41998         "Nicaragua",
41999         "ni",
42000         "505"
42001       ],
42002       [
42003         "Niger (Nijar)",
42004         "ne",
42005         "227"
42006       ],
42007       [
42008         "Nigeria",
42009         "ng",
42010         "234"
42011       ],
42012       [
42013         "Niue",
42014         "nu",
42015         "683"
42016       ],
42017       [
42018         "Norfolk Island",
42019         "nf",
42020         "672"
42021       ],
42022       [
42023         "North Korea (조선 민주주의 인민 공화국)",
42024         "kp",
42025         "850"
42026       ],
42027       [
42028         "Northern Mariana Islands",
42029         "mp",
42030         "1670"
42031       ],
42032       [
42033         "Norway (Norge)",
42034         "no",
42035         "47",
42036         0
42037       ],
42038       [
42039         "Oman (‫عُمان‬‎)",
42040         "om",
42041         "968"
42042       ],
42043       [
42044         "Pakistan (‫پاکستان‬‎)",
42045         "pk",
42046         "92"
42047       ],
42048       [
42049         "Palau",
42050         "pw",
42051         "680"
42052       ],
42053       [
42054         "Palestine (‫فلسطين‬‎)",
42055         "ps",
42056         "970"
42057       ],
42058       [
42059         "Panama (Panamá)",
42060         "pa",
42061         "507"
42062       ],
42063       [
42064         "Papua New Guinea",
42065         "pg",
42066         "675"
42067       ],
42068       [
42069         "Paraguay",
42070         "py",
42071         "595"
42072       ],
42073       [
42074         "Peru (Perú)",
42075         "pe",
42076         "51"
42077       ],
42078       [
42079         "Philippines",
42080         "ph",
42081         "63"
42082       ],
42083       [
42084         "Poland (Polska)",
42085         "pl",
42086         "48"
42087       ],
42088       [
42089         "Portugal",
42090         "pt",
42091         "351"
42092       ],
42093       [
42094         "Puerto Rico",
42095         "pr",
42096         "1",
42097         3,
42098         ["787", "939"]
42099       ],
42100       [
42101         "Qatar (‫قطر‬‎)",
42102         "qa",
42103         "974"
42104       ],
42105       [
42106         "Réunion (La Réunion)",
42107         "re",
42108         "262",
42109         0
42110       ],
42111       [
42112         "Romania (România)",
42113         "ro",
42114         "40"
42115       ],
42116       [
42117         "Russia (Россия)",
42118         "ru",
42119         "7",
42120         0
42121       ],
42122       [
42123         "Rwanda",
42124         "rw",
42125         "250"
42126       ],
42127       [
42128         "Saint Barthélemy",
42129         "bl",
42130         "590",
42131         1
42132       ],
42133       [
42134         "Saint Helena",
42135         "sh",
42136         "290"
42137       ],
42138       [
42139         "Saint Kitts and Nevis",
42140         "kn",
42141         "1869"
42142       ],
42143       [
42144         "Saint Lucia",
42145         "lc",
42146         "1758"
42147       ],
42148       [
42149         "Saint Martin (Saint-Martin (partie française))",
42150         "mf",
42151         "590",
42152         2
42153       ],
42154       [
42155         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42156         "pm",
42157         "508"
42158       ],
42159       [
42160         "Saint Vincent and the Grenadines",
42161         "vc",
42162         "1784"
42163       ],
42164       [
42165         "Samoa",
42166         "ws",
42167         "685"
42168       ],
42169       [
42170         "San Marino",
42171         "sm",
42172         "378"
42173       ],
42174       [
42175         "São Tomé and Príncipe (São Tomé e Príncipe)",
42176         "st",
42177         "239"
42178       ],
42179       [
42180         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42181         "sa",
42182         "966"
42183       ],
42184       [
42185         "Senegal (Sénégal)",
42186         "sn",
42187         "221"
42188       ],
42189       [
42190         "Serbia (Србија)",
42191         "rs",
42192         "381"
42193       ],
42194       [
42195         "Seychelles",
42196         "sc",
42197         "248"
42198       ],
42199       [
42200         "Sierra Leone",
42201         "sl",
42202         "232"
42203       ],
42204       [
42205         "Singapore",
42206         "sg",
42207         "65"
42208       ],
42209       [
42210         "Sint Maarten",
42211         "sx",
42212         "1721"
42213       ],
42214       [
42215         "Slovakia (Slovensko)",
42216         "sk",
42217         "421"
42218       ],
42219       [
42220         "Slovenia (Slovenija)",
42221         "si",
42222         "386"
42223       ],
42224       [
42225         "Solomon Islands",
42226         "sb",
42227         "677"
42228       ],
42229       [
42230         "Somalia (Soomaaliya)",
42231         "so",
42232         "252"
42233       ],
42234       [
42235         "South Africa",
42236         "za",
42237         "27"
42238       ],
42239       [
42240         "South Korea (대한민국)",
42241         "kr",
42242         "82"
42243       ],
42244       [
42245         "South Sudan (‫جنوب السودان‬‎)",
42246         "ss",
42247         "211"
42248       ],
42249       [
42250         "Spain (España)",
42251         "es",
42252         "34"
42253       ],
42254       [
42255         "Sri Lanka (ශ්‍රී ලංකාව)",
42256         "lk",
42257         "94"
42258       ],
42259       [
42260         "Sudan (‫السودان‬‎)",
42261         "sd",
42262         "249"
42263       ],
42264       [
42265         "Suriname",
42266         "sr",
42267         "597"
42268       ],
42269       [
42270         "Svalbard and Jan Mayen",
42271         "sj",
42272         "47",
42273         1
42274       ],
42275       [
42276         "Swaziland",
42277         "sz",
42278         "268"
42279       ],
42280       [
42281         "Sweden (Sverige)",
42282         "se",
42283         "46"
42284       ],
42285       [
42286         "Switzerland (Schweiz)",
42287         "ch",
42288         "41"
42289       ],
42290       [
42291         "Syria (‫سوريا‬‎)",
42292         "sy",
42293         "963"
42294       ],
42295       [
42296         "Taiwan (台灣)",
42297         "tw",
42298         "886"
42299       ],
42300       [
42301         "Tajikistan",
42302         "tj",
42303         "992"
42304       ],
42305       [
42306         "Tanzania",
42307         "tz",
42308         "255"
42309       ],
42310       [
42311         "Thailand (ไทย)",
42312         "th",
42313         "66"
42314       ],
42315       [
42316         "Timor-Leste",
42317         "tl",
42318         "670"
42319       ],
42320       [
42321         "Togo",
42322         "tg",
42323         "228"
42324       ],
42325       [
42326         "Tokelau",
42327         "tk",
42328         "690"
42329       ],
42330       [
42331         "Tonga",
42332         "to",
42333         "676"
42334       ],
42335       [
42336         "Trinidad and Tobago",
42337         "tt",
42338         "1868"
42339       ],
42340       [
42341         "Tunisia (‫تونس‬‎)",
42342         "tn",
42343         "216"
42344       ],
42345       [
42346         "Turkey (Türkiye)",
42347         "tr",
42348         "90"
42349       ],
42350       [
42351         "Turkmenistan",
42352         "tm",
42353         "993"
42354       ],
42355       [
42356         "Turks and Caicos Islands",
42357         "tc",
42358         "1649"
42359       ],
42360       [
42361         "Tuvalu",
42362         "tv",
42363         "688"
42364       ],
42365       [
42366         "U.S. Virgin Islands",
42367         "vi",
42368         "1340"
42369       ],
42370       [
42371         "Uganda",
42372         "ug",
42373         "256"
42374       ],
42375       [
42376         "Ukraine (Україна)",
42377         "ua",
42378         "380"
42379       ],
42380       [
42381         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42382         "ae",
42383         "971"
42384       ],
42385       [
42386         "United Kingdom",
42387         "gb",
42388         "44",
42389         0
42390       ],
42391       [
42392         "United States",
42393         "us",
42394         "1",
42395         0
42396       ],
42397       [
42398         "Uruguay",
42399         "uy",
42400         "598"
42401       ],
42402       [
42403         "Uzbekistan (Oʻzbekiston)",
42404         "uz",
42405         "998"
42406       ],
42407       [
42408         "Vanuatu",
42409         "vu",
42410         "678"
42411       ],
42412       [
42413         "Vatican City (Città del Vaticano)",
42414         "va",
42415         "39",
42416         1
42417       ],
42418       [
42419         "Venezuela",
42420         "ve",
42421         "58"
42422       ],
42423       [
42424         "Vietnam (Việt Nam)",
42425         "vn",
42426         "84"
42427       ],
42428       [
42429         "Wallis and Futuna (Wallis-et-Futuna)",
42430         "wf",
42431         "681"
42432       ],
42433       [
42434         "Western Sahara (‫الصحراء الغربية‬‎)",
42435         "eh",
42436         "212",
42437         1
42438       ],
42439       [
42440         "Yemen (‫اليمن‬‎)",
42441         "ye",
42442         "967"
42443       ],
42444       [
42445         "Zambia",
42446         "zm",
42447         "260"
42448       ],
42449       [
42450         "Zimbabwe",
42451         "zw",
42452         "263"
42453       ],
42454       [
42455         "Åland Islands",
42456         "ax",
42457         "358",
42458         1
42459       ]
42460   ];
42461   
42462   return d;
42463 }/**
42464 *    This script refer to:
42465 *    Title: International Telephone Input
42466 *    Author: Jack O'Connor
42467 *    Code version:  v12.1.12
42468 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42469 **/
42470
42471 /**
42472  * @class Roo.bootstrap.PhoneInput
42473  * @extends Roo.bootstrap.TriggerField
42474  * An input with International dial-code selection
42475  
42476  * @cfg {String} defaultDialCode default '+852'
42477  * @cfg {Array} preferedCountries default []
42478   
42479  * @constructor
42480  * Create a new PhoneInput.
42481  * @param {Object} config Configuration options
42482  */
42483
42484 Roo.bootstrap.PhoneInput = function(config) {
42485     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42486 };
42487
42488 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42489         
42490         listWidth: undefined,
42491         
42492         selectedClass: 'active',
42493         
42494         invalidClass : "has-warning",
42495         
42496         validClass: 'has-success',
42497         
42498         allowed: '0123456789',
42499         
42500         max_length: 15,
42501         
42502         /**
42503          * @cfg {String} defaultDialCode The default dial code when initializing the input
42504          */
42505         defaultDialCode: '+852',
42506         
42507         /**
42508          * @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
42509          */
42510         preferedCountries: false,
42511         
42512         getAutoCreate : function()
42513         {
42514             var data = Roo.bootstrap.PhoneInputData();
42515             var align = this.labelAlign || this.parentLabelAlign();
42516             var id = Roo.id();
42517             
42518             this.allCountries = [];
42519             this.dialCodeMapping = [];
42520             
42521             for (var i = 0; i < data.length; i++) {
42522               var c = data[i];
42523               this.allCountries[i] = {
42524                 name: c[0],
42525                 iso2: c[1],
42526                 dialCode: c[2],
42527                 priority: c[3] || 0,
42528                 areaCodes: c[4] || null
42529               };
42530               this.dialCodeMapping[c[2]] = {
42531                   name: c[0],
42532                   iso2: c[1],
42533                   priority: c[3] || 0,
42534                   areaCodes: c[4] || null
42535               };
42536             }
42537             
42538             var cfg = {
42539                 cls: 'form-group',
42540                 cn: []
42541             };
42542             
42543             var input =  {
42544                 tag: 'input',
42545                 id : id,
42546                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42547                 maxlength: this.max_length,
42548                 cls : 'form-control tel-input',
42549                 autocomplete: 'new-password'
42550             };
42551             
42552             var hiddenInput = {
42553                 tag: 'input',
42554                 type: 'hidden',
42555                 cls: 'hidden-tel-input'
42556             };
42557             
42558             if (this.name) {
42559                 hiddenInput.name = this.name;
42560             }
42561             
42562             if (this.disabled) {
42563                 input.disabled = true;
42564             }
42565             
42566             var flag_container = {
42567                 tag: 'div',
42568                 cls: 'flag-box',
42569                 cn: [
42570                     {
42571                         tag: 'div',
42572                         cls: 'flag'
42573                     },
42574                     {
42575                         tag: 'div',
42576                         cls: 'caret'
42577                     }
42578                 ]
42579             };
42580             
42581             var box = {
42582                 tag: 'div',
42583                 cls: this.hasFeedback ? 'has-feedback' : '',
42584                 cn: [
42585                     hiddenInput,
42586                     input,
42587                     {
42588                         tag: 'input',
42589                         cls: 'dial-code-holder',
42590                         disabled: true
42591                     }
42592                 ]
42593             };
42594             
42595             var container = {
42596                 cls: 'roo-select2-container input-group',
42597                 cn: [
42598                     flag_container,
42599                     box
42600                 ]
42601             };
42602             
42603             if (this.fieldLabel.length) {
42604                 var indicator = {
42605                     tag: 'i',
42606                     tooltip: 'This field is required'
42607                 };
42608                 
42609                 var label = {
42610                     tag: 'label',
42611                     'for':  id,
42612                     cls: 'control-label',
42613                     cn: []
42614                 };
42615                 
42616                 var label_text = {
42617                     tag: 'span',
42618                     html: this.fieldLabel
42619                 };
42620                 
42621                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42622                 label.cn = [
42623                     indicator,
42624                     label_text
42625                 ];
42626                 
42627                 if(this.indicatorpos == 'right') {
42628                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42629                     label.cn = [
42630                         label_text,
42631                         indicator
42632                     ];
42633                 }
42634                 
42635                 if(align == 'left') {
42636                     container = {
42637                         tag: 'div',
42638                         cn: [
42639                             container
42640                         ]
42641                     };
42642                     
42643                     if(this.labelWidth > 12){
42644                         label.style = "width: " + this.labelWidth + 'px';
42645                     }
42646                     if(this.labelWidth < 13 && this.labelmd == 0){
42647                         this.labelmd = this.labelWidth;
42648                     }
42649                     if(this.labellg > 0){
42650                         label.cls += ' col-lg-' + this.labellg;
42651                         input.cls += ' col-lg-' + (12 - this.labellg);
42652                     }
42653                     if(this.labelmd > 0){
42654                         label.cls += ' col-md-' + this.labelmd;
42655                         container.cls += ' col-md-' + (12 - this.labelmd);
42656                     }
42657                     if(this.labelsm > 0){
42658                         label.cls += ' col-sm-' + this.labelsm;
42659                         container.cls += ' col-sm-' + (12 - this.labelsm);
42660                     }
42661                     if(this.labelxs > 0){
42662                         label.cls += ' col-xs-' + this.labelxs;
42663                         container.cls += ' col-xs-' + (12 - this.labelxs);
42664                     }
42665                 }
42666             }
42667             
42668             cfg.cn = [
42669                 label,
42670                 container
42671             ];
42672             
42673             var settings = this;
42674             
42675             ['xs','sm','md','lg'].map(function(size){
42676                 if (settings[size]) {
42677                     cfg.cls += ' col-' + size + '-' + settings[size];
42678                 }
42679             });
42680             
42681             this.store = new Roo.data.Store({
42682                 proxy : new Roo.data.MemoryProxy({}),
42683                 reader : new Roo.data.JsonReader({
42684                     fields : [
42685                         {
42686                             'name' : 'name',
42687                             'type' : 'string'
42688                         },
42689                         {
42690                             'name' : 'iso2',
42691                             'type' : 'string'
42692                         },
42693                         {
42694                             'name' : 'dialCode',
42695                             'type' : 'string'
42696                         },
42697                         {
42698                             'name' : 'priority',
42699                             'type' : 'string'
42700                         },
42701                         {
42702                             'name' : 'areaCodes',
42703                             'type' : 'string'
42704                         }
42705                     ]
42706                 })
42707             });
42708             
42709             if(!this.preferedCountries) {
42710                 this.preferedCountries = [
42711                     'hk',
42712                     'gb',
42713                     'us'
42714                 ];
42715             }
42716             
42717             var p = this.preferedCountries.reverse();
42718             
42719             if(p) {
42720                 for (var i = 0; i < p.length; i++) {
42721                     for (var j = 0; j < this.allCountries.length; j++) {
42722                         if(this.allCountries[j].iso2 == p[i]) {
42723                             var t = this.allCountries[j];
42724                             this.allCountries.splice(j,1);
42725                             this.allCountries.unshift(t);
42726                         }
42727                     } 
42728                 }
42729             }
42730             
42731             this.store.proxy.data = {
42732                 success: true,
42733                 data: this.allCountries
42734             };
42735             
42736             return cfg;
42737         },
42738         
42739         initEvents : function()
42740         {
42741             this.createList();
42742             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42743             
42744             this.indicator = this.indicatorEl();
42745             this.flag = this.flagEl();
42746             this.dialCodeHolder = this.dialCodeHolderEl();
42747             
42748             this.trigger = this.el.select('div.flag-box',true).first();
42749             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42750             
42751             var _this = this;
42752             
42753             (function(){
42754                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42755                 _this.list.setWidth(lw);
42756             }).defer(100);
42757             
42758             this.list.on('mouseover', this.onViewOver, this);
42759             this.list.on('mousemove', this.onViewMove, this);
42760             this.inputEl().on("keyup", this.onKeyUp, this);
42761             this.inputEl().on("keypress", this.onKeyPress, this);
42762             
42763             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42764
42765             this.view = new Roo.View(this.list, this.tpl, {
42766                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42767             });
42768             
42769             this.view.on('click', this.onViewClick, this);
42770             this.setValue(this.defaultDialCode);
42771         },
42772         
42773         onTriggerClick : function(e)
42774         {
42775             Roo.log('trigger click');
42776             if(this.disabled){
42777                 return;
42778             }
42779             
42780             if(this.isExpanded()){
42781                 this.collapse();
42782                 this.hasFocus = false;
42783             }else {
42784                 this.store.load({});
42785                 this.hasFocus = true;
42786                 this.expand();
42787             }
42788         },
42789         
42790         isExpanded : function()
42791         {
42792             return this.list.isVisible();
42793         },
42794         
42795         collapse : function()
42796         {
42797             if(!this.isExpanded()){
42798                 return;
42799             }
42800             this.list.hide();
42801             Roo.get(document).un('mousedown', this.collapseIf, this);
42802             Roo.get(document).un('mousewheel', this.collapseIf, this);
42803             this.fireEvent('collapse', this);
42804             this.validate();
42805         },
42806         
42807         expand : function()
42808         {
42809             Roo.log('expand');
42810
42811             if(this.isExpanded() || !this.hasFocus){
42812                 return;
42813             }
42814             
42815             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42816             this.list.setWidth(lw);
42817             
42818             this.list.show();
42819             this.restrictHeight();
42820             
42821             Roo.get(document).on('mousedown', this.collapseIf, this);
42822             Roo.get(document).on('mousewheel', this.collapseIf, this);
42823             
42824             this.fireEvent('expand', this);
42825         },
42826         
42827         restrictHeight : function()
42828         {
42829             this.list.alignTo(this.inputEl(), this.listAlign);
42830             this.list.alignTo(this.inputEl(), this.listAlign);
42831         },
42832         
42833         onViewOver : function(e, t)
42834         {
42835             if(this.inKeyMode){
42836                 return;
42837             }
42838             var item = this.view.findItemFromChild(t);
42839             
42840             if(item){
42841                 var index = this.view.indexOf(item);
42842                 this.select(index, false);
42843             }
42844         },
42845
42846         // private
42847         onViewClick : function(view, doFocus, el, e)
42848         {
42849             var index = this.view.getSelectedIndexes()[0];
42850             
42851             var r = this.store.getAt(index);
42852             
42853             if(r){
42854                 this.onSelect(r, index);
42855             }
42856             if(doFocus !== false && !this.blockFocus){
42857                 this.inputEl().focus();
42858             }
42859         },
42860         
42861         onViewMove : function(e, t)
42862         {
42863             this.inKeyMode = false;
42864         },
42865         
42866         select : function(index, scrollIntoView)
42867         {
42868             this.selectedIndex = index;
42869             this.view.select(index);
42870             if(scrollIntoView !== false){
42871                 var el = this.view.getNode(index);
42872                 if(el){
42873                     this.list.scrollChildIntoView(el, false);
42874                 }
42875             }
42876         },
42877         
42878         createList : function()
42879         {
42880             this.list = Roo.get(document.body).createChild({
42881                 tag: 'ul',
42882                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42883                 style: 'display:none'
42884             });
42885             
42886             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42887         },
42888         
42889         collapseIf : function(e)
42890         {
42891             var in_combo  = e.within(this.el);
42892             var in_list =  e.within(this.list);
42893             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42894             
42895             if (in_combo || in_list || is_list) {
42896                 return;
42897             }
42898             this.collapse();
42899         },
42900         
42901         onSelect : function(record, index)
42902         {
42903             if(this.fireEvent('beforeselect', this, record, index) !== false){
42904                 
42905                 this.setFlagClass(record.data.iso2);
42906                 this.setDialCode(record.data.dialCode);
42907                 this.hasFocus = false;
42908                 this.collapse();
42909                 this.fireEvent('select', this, record, index);
42910             }
42911         },
42912         
42913         flagEl : function()
42914         {
42915             var flag = this.el.select('div.flag',true).first();
42916             if(!flag){
42917                 return false;
42918             }
42919             return flag;
42920         },
42921         
42922         dialCodeHolderEl : function()
42923         {
42924             var d = this.el.select('input.dial-code-holder',true).first();
42925             if(!d){
42926                 return false;
42927             }
42928             return d;
42929         },
42930         
42931         setDialCode : function(v)
42932         {
42933             this.dialCodeHolder.dom.value = '+'+v;
42934         },
42935         
42936         setFlagClass : function(n)
42937         {
42938             this.flag.dom.className = 'flag '+n;
42939         },
42940         
42941         getValue : function()
42942         {
42943             var v = this.inputEl().getValue();
42944             if(this.dialCodeHolder) {
42945                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42946             }
42947             return v;
42948         },
42949         
42950         setValue : function(v)
42951         {
42952             var d = this.getDialCode(v);
42953             
42954             //invalid dial code
42955             if(v.length == 0 || !d || d.length == 0) {
42956                 if(this.rendered){
42957                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42958                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42959                 }
42960                 return;
42961             }
42962             
42963             //valid dial code
42964             this.setFlagClass(this.dialCodeMapping[d].iso2);
42965             this.setDialCode(d);
42966             this.inputEl().dom.value = v.replace('+'+d,'');
42967             this.hiddenEl().dom.value = this.getValue();
42968             
42969             this.validate();
42970         },
42971         
42972         getDialCode : function(v)
42973         {
42974             v = v ||  '';
42975             
42976             if (v.length == 0) {
42977                 return this.dialCodeHolder.dom.value;
42978             }
42979             
42980             var dialCode = "";
42981             if (v.charAt(0) != "+") {
42982                 return false;
42983             }
42984             var numericChars = "";
42985             for (var i = 1; i < v.length; i++) {
42986               var c = v.charAt(i);
42987               if (!isNaN(c)) {
42988                 numericChars += c;
42989                 if (this.dialCodeMapping[numericChars]) {
42990                   dialCode = v.substr(1, i);
42991                 }
42992                 if (numericChars.length == 4) {
42993                   break;
42994                 }
42995               }
42996             }
42997             return dialCode;
42998         },
42999         
43000         reset : function()
43001         {
43002             this.setValue(this.defaultDialCode);
43003             this.validate();
43004         },
43005         
43006         hiddenEl : function()
43007         {
43008             return this.el.select('input.hidden-tel-input',true).first();
43009         },
43010         
43011         // after setting val
43012         onKeyUp : function(e){
43013             this.setValue(this.getValue());
43014         },
43015         
43016         onKeyPress : function(e){
43017             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43018                 e.stopEvent();
43019             }
43020         }
43021         
43022 });
43023 /**
43024  * @class Roo.bootstrap.MoneyField
43025  * @extends Roo.bootstrap.ComboBox
43026  * Bootstrap MoneyField class
43027  * 
43028  * @constructor
43029  * Create a new MoneyField.
43030  * @param {Object} config Configuration options
43031  */
43032
43033 Roo.bootstrap.MoneyField = function(config) {
43034     
43035     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43036     
43037 };
43038
43039 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43040     
43041     /**
43042      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43043      */
43044     allowDecimals : true,
43045     /**
43046      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43047      */
43048     decimalSeparator : ".",
43049     /**
43050      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43051      */
43052     decimalPrecision : 0,
43053     /**
43054      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43055      */
43056     allowNegative : true,
43057     /**
43058      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43059      */
43060     allowZero: true,
43061     /**
43062      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43063      */
43064     minValue : Number.NEGATIVE_INFINITY,
43065     /**
43066      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43067      */
43068     maxValue : Number.MAX_VALUE,
43069     /**
43070      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43071      */
43072     minText : "The minimum value for this field is {0}",
43073     /**
43074      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43075      */
43076     maxText : "The maximum value for this field is {0}",
43077     /**
43078      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43079      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43080      */
43081     nanText : "{0} is not a valid number",
43082     /**
43083      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43084      */
43085     castInt : true,
43086     /**
43087      * @cfg {String} defaults currency of the MoneyField
43088      * value should be in lkey
43089      */
43090     defaultCurrency : false,
43091     /**
43092      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43093      */
43094     thousandsDelimiter : false,
43095     /**
43096      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43097      */
43098     max_length: false,
43099     
43100     inputlg : 9,
43101     inputmd : 9,
43102     inputsm : 9,
43103     inputxs : 6,
43104     
43105     store : false,
43106     
43107     getAutoCreate : function()
43108     {
43109         var align = this.labelAlign || this.parentLabelAlign();
43110         
43111         var id = Roo.id();
43112
43113         var cfg = {
43114             cls: 'form-group',
43115             cn: []
43116         };
43117
43118         var input =  {
43119             tag: 'input',
43120             id : id,
43121             cls : 'form-control roo-money-amount-input',
43122             autocomplete: 'new-password'
43123         };
43124         
43125         var hiddenInput = {
43126             tag: 'input',
43127             type: 'hidden',
43128             id: Roo.id(),
43129             cls: 'hidden-number-input'
43130         };
43131         
43132         if(this.max_length) {
43133             input.maxlength = this.max_length; 
43134         }
43135         
43136         if (this.name) {
43137             hiddenInput.name = this.name;
43138         }
43139
43140         if (this.disabled) {
43141             input.disabled = true;
43142         }
43143
43144         var clg = 12 - this.inputlg;
43145         var cmd = 12 - this.inputmd;
43146         var csm = 12 - this.inputsm;
43147         var cxs = 12 - this.inputxs;
43148         
43149         var container = {
43150             tag : 'div',
43151             cls : 'row roo-money-field',
43152             cn : [
43153                 {
43154                     tag : 'div',
43155                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43156                     cn : [
43157                         {
43158                             tag : 'div',
43159                             cls: 'roo-select2-container input-group',
43160                             cn: [
43161                                 {
43162                                     tag : 'input',
43163                                     cls : 'form-control roo-money-currency-input',
43164                                     autocomplete: 'new-password',
43165                                     readOnly : 1,
43166                                     name : this.currencyName
43167                                 },
43168                                 {
43169                                     tag :'span',
43170                                     cls : 'input-group-addon',
43171                                     cn : [
43172                                         {
43173                                             tag: 'span',
43174                                             cls: 'caret'
43175                                         }
43176                                     ]
43177                                 }
43178                             ]
43179                         }
43180                     ]
43181                 },
43182                 {
43183                     tag : 'div',
43184                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43185                     cn : [
43186                         {
43187                             tag: 'div',
43188                             cls: this.hasFeedback ? 'has-feedback' : '',
43189                             cn: [
43190                                 input
43191                             ]
43192                         }
43193                     ]
43194                 }
43195             ]
43196             
43197         };
43198         
43199         if (this.fieldLabel.length) {
43200             var indicator = {
43201                 tag: 'i',
43202                 tooltip: 'This field is required'
43203             };
43204
43205             var label = {
43206                 tag: 'label',
43207                 'for':  id,
43208                 cls: 'control-label',
43209                 cn: []
43210             };
43211
43212             var label_text = {
43213                 tag: 'span',
43214                 html: this.fieldLabel
43215             };
43216
43217             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43218             label.cn = [
43219                 indicator,
43220                 label_text
43221             ];
43222
43223             if(this.indicatorpos == 'right') {
43224                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43225                 label.cn = [
43226                     label_text,
43227                     indicator
43228                 ];
43229             }
43230
43231             if(align == 'left') {
43232                 container = {
43233                     tag: 'div',
43234                     cn: [
43235                         container
43236                     ]
43237                 };
43238
43239                 if(this.labelWidth > 12){
43240                     label.style = "width: " + this.labelWidth + 'px';
43241                 }
43242                 if(this.labelWidth < 13 && this.labelmd == 0){
43243                     this.labelmd = this.labelWidth;
43244                 }
43245                 if(this.labellg > 0){
43246                     label.cls += ' col-lg-' + this.labellg;
43247                     input.cls += ' col-lg-' + (12 - this.labellg);
43248                 }
43249                 if(this.labelmd > 0){
43250                     label.cls += ' col-md-' + this.labelmd;
43251                     container.cls += ' col-md-' + (12 - this.labelmd);
43252                 }
43253                 if(this.labelsm > 0){
43254                     label.cls += ' col-sm-' + this.labelsm;
43255                     container.cls += ' col-sm-' + (12 - this.labelsm);
43256                 }
43257                 if(this.labelxs > 0){
43258                     label.cls += ' col-xs-' + this.labelxs;
43259                     container.cls += ' col-xs-' + (12 - this.labelxs);
43260                 }
43261             }
43262         }
43263
43264         cfg.cn = [
43265             label,
43266             container,
43267             hiddenInput
43268         ];
43269         
43270         var settings = this;
43271
43272         ['xs','sm','md','lg'].map(function(size){
43273             if (settings[size]) {
43274                 cfg.cls += ' col-' + size + '-' + settings[size];
43275             }
43276         });
43277         
43278         return cfg;
43279     },
43280     
43281     initEvents : function()
43282     {
43283         this.indicator = this.indicatorEl();
43284         
43285         this.initCurrencyEvent();
43286         
43287         this.initNumberEvent();
43288     },
43289     
43290     initCurrencyEvent : function()
43291     {
43292         if (!this.store) {
43293             throw "can not find store for combo";
43294         }
43295         
43296         this.store = Roo.factory(this.store, Roo.data);
43297         this.store.parent = this;
43298         
43299         this.createList();
43300         
43301         this.triggerEl = this.el.select('.input-group-addon', true).first();
43302         
43303         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43304         
43305         var _this = this;
43306         
43307         (function(){
43308             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43309             _this.list.setWidth(lw);
43310         }).defer(100);
43311         
43312         this.list.on('mouseover', this.onViewOver, this);
43313         this.list.on('mousemove', this.onViewMove, this);
43314         this.list.on('scroll', this.onViewScroll, this);
43315         
43316         if(!this.tpl){
43317             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43318         }
43319         
43320         this.view = new Roo.View(this.list, this.tpl, {
43321             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43322         });
43323         
43324         this.view.on('click', this.onViewClick, this);
43325         
43326         this.store.on('beforeload', this.onBeforeLoad, this);
43327         this.store.on('load', this.onLoad, this);
43328         this.store.on('loadexception', this.onLoadException, this);
43329         
43330         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43331             "up" : function(e){
43332                 this.inKeyMode = true;
43333                 this.selectPrev();
43334             },
43335
43336             "down" : function(e){
43337                 if(!this.isExpanded()){
43338                     this.onTriggerClick();
43339                 }else{
43340                     this.inKeyMode = true;
43341                     this.selectNext();
43342                 }
43343             },
43344
43345             "enter" : function(e){
43346                 this.collapse();
43347                 
43348                 if(this.fireEvent("specialkey", this, e)){
43349                     this.onViewClick(false);
43350                 }
43351                 
43352                 return true;
43353             },
43354
43355             "esc" : function(e){
43356                 this.collapse();
43357             },
43358
43359             "tab" : function(e){
43360                 this.collapse();
43361                 
43362                 if(this.fireEvent("specialkey", this, e)){
43363                     this.onViewClick(false);
43364                 }
43365                 
43366                 return true;
43367             },
43368
43369             scope : this,
43370
43371             doRelay : function(foo, bar, hname){
43372                 if(hname == 'down' || this.scope.isExpanded()){
43373                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43374                 }
43375                 return true;
43376             },
43377
43378             forceKeyDown: true
43379         });
43380         
43381         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43382         
43383     },
43384     
43385     initNumberEvent : function(e)
43386     {
43387         this.inputEl().on("keydown" , this.fireKey,  this);
43388         this.inputEl().on("focus", this.onFocus,  this);
43389         this.inputEl().on("blur", this.onBlur,  this);
43390         
43391         this.inputEl().relayEvent('keyup', this);
43392         
43393         if(this.indicator){
43394             this.indicator.addClass('invisible');
43395         }
43396  
43397         this.originalValue = this.getValue();
43398         
43399         if(this.validationEvent == 'keyup'){
43400             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43401             this.inputEl().on('keyup', this.filterValidation, this);
43402         }
43403         else if(this.validationEvent !== false){
43404             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43405         }
43406         
43407         if(this.selectOnFocus){
43408             this.on("focus", this.preFocus, this);
43409             
43410         }
43411         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43412             this.inputEl().on("keypress", this.filterKeys, this);
43413         } else {
43414             this.inputEl().relayEvent('keypress', this);
43415         }
43416         
43417         var allowed = "0123456789";
43418         
43419         if(this.allowDecimals){
43420             allowed += this.decimalSeparator;
43421         }
43422         
43423         if(this.allowNegative){
43424             allowed += "-";
43425         }
43426         
43427         if(this.thousandsDelimiter) {
43428             allowed += ",";
43429         }
43430         
43431         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43432         
43433         var keyPress = function(e){
43434             
43435             var k = e.getKey();
43436             
43437             var c = e.getCharCode();
43438             
43439             if(
43440                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43441                     allowed.indexOf(String.fromCharCode(c)) === -1
43442             ){
43443                 e.stopEvent();
43444                 return;
43445             }
43446             
43447             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43448                 return;
43449             }
43450             
43451             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43452                 e.stopEvent();
43453             }
43454         };
43455         
43456         this.inputEl().on("keypress", keyPress, this);
43457         
43458     },
43459     
43460     onTriggerClick : function(e)
43461     {   
43462         if(this.disabled){
43463             return;
43464         }
43465         
43466         this.page = 0;
43467         this.loadNext = false;
43468         
43469         if(this.isExpanded()){
43470             this.collapse();
43471             return;
43472         }
43473         
43474         this.hasFocus = true;
43475         
43476         if(this.triggerAction == 'all') {
43477             this.doQuery(this.allQuery, true);
43478             return;
43479         }
43480         
43481         this.doQuery(this.getRawValue());
43482     },
43483     
43484     getCurrency : function()
43485     {   
43486         var v = this.currencyEl().getValue();
43487         
43488         return v;
43489     },
43490     
43491     restrictHeight : function()
43492     {
43493         this.list.alignTo(this.currencyEl(), this.listAlign);
43494         this.list.alignTo(this.currencyEl(), this.listAlign);
43495     },
43496     
43497     onViewClick : function(view, doFocus, el, e)
43498     {
43499         var index = this.view.getSelectedIndexes()[0];
43500         
43501         var r = this.store.getAt(index);
43502         
43503         if(r){
43504             this.onSelect(r, index);
43505         }
43506     },
43507     
43508     onSelect : function(record, index){
43509         
43510         if(this.fireEvent('beforeselect', this, record, index) !== false){
43511         
43512             this.setFromCurrencyData(index > -1 ? record.data : false);
43513             
43514             this.collapse();
43515             
43516             this.fireEvent('select', this, record, index);
43517         }
43518     },
43519     
43520     setFromCurrencyData : function(o)
43521     {
43522         var currency = '';
43523         
43524         this.lastCurrency = o;
43525         
43526         if (this.currencyField) {
43527             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43528         } else {
43529             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43530         }
43531         
43532         this.lastSelectionText = currency;
43533         
43534         //setting default currency
43535         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43536             this.setCurrency(this.defaultCurrency);
43537             return;
43538         }
43539         
43540         this.setCurrency(currency);
43541     },
43542     
43543     setFromData : function(o)
43544     {
43545         var c = {};
43546         
43547         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43548         
43549         this.setFromCurrencyData(c);
43550         
43551         var value = '';
43552         
43553         if (this.name) {
43554             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43555         } else {
43556             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43557         }
43558         
43559         this.setValue(value);
43560         
43561     },
43562     
43563     setCurrency : function(v)
43564     {   
43565         this.currencyValue = v;
43566         
43567         if(this.rendered){
43568             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43569             this.validate();
43570         }
43571     },
43572     
43573     setValue : function(v)
43574     {
43575         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43576         
43577         this.value = v;
43578         
43579         if(this.rendered){
43580             
43581             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43582             
43583             this.inputEl().dom.value = (v == '') ? '' :
43584                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43585             
43586             if(!this.allowZero && v === '0') {
43587                 this.hiddenEl().dom.value = '';
43588                 this.inputEl().dom.value = '';
43589             }
43590             
43591             this.validate();
43592         }
43593     },
43594     
43595     getRawValue : function()
43596     {
43597         var v = this.inputEl().getValue();
43598         
43599         return v;
43600     },
43601     
43602     getValue : function()
43603     {
43604         return this.fixPrecision(this.parseValue(this.getRawValue()));
43605     },
43606     
43607     parseValue : function(value)
43608     {
43609         if(this.thousandsDelimiter) {
43610             value += "";
43611             r = new RegExp(",", "g");
43612             value = value.replace(r, "");
43613         }
43614         
43615         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43616         return isNaN(value) ? '' : value;
43617         
43618     },
43619     
43620     fixPrecision : function(value)
43621     {
43622         if(this.thousandsDelimiter) {
43623             value += "";
43624             r = new RegExp(",", "g");
43625             value = value.replace(r, "");
43626         }
43627         
43628         var nan = isNaN(value);
43629         
43630         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43631             return nan ? '' : value;
43632         }
43633         return parseFloat(value).toFixed(this.decimalPrecision);
43634     },
43635     
43636     decimalPrecisionFcn : function(v)
43637     {
43638         return Math.floor(v);
43639     },
43640     
43641     validateValue : function(value)
43642     {
43643         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43644             return false;
43645         }
43646         
43647         var num = this.parseValue(value);
43648         
43649         if(isNaN(num)){
43650             this.markInvalid(String.format(this.nanText, value));
43651             return false;
43652         }
43653         
43654         if(num < this.minValue){
43655             this.markInvalid(String.format(this.minText, this.minValue));
43656             return false;
43657         }
43658         
43659         if(num > this.maxValue){
43660             this.markInvalid(String.format(this.maxText, this.maxValue));
43661             return false;
43662         }
43663         
43664         return true;
43665     },
43666     
43667     validate : function()
43668     {
43669         if(this.disabled || this.allowBlank){
43670             this.markValid();
43671             return true;
43672         }
43673         
43674         var currency = this.getCurrency();
43675         
43676         if(this.validateValue(this.getRawValue()) && currency.length){
43677             this.markValid();
43678             return true;
43679         }
43680         
43681         this.markInvalid();
43682         return false;
43683     },
43684     
43685     getName: function()
43686     {
43687         return this.name;
43688     },
43689     
43690     beforeBlur : function()
43691     {
43692         if(!this.castInt){
43693             return;
43694         }
43695         
43696         var v = this.parseValue(this.getRawValue());
43697         
43698         if(v || v == 0){
43699             this.setValue(v);
43700         }
43701     },
43702     
43703     onBlur : function()
43704     {
43705         this.beforeBlur();
43706         
43707         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43708             //this.el.removeClass(this.focusClass);
43709         }
43710         
43711         this.hasFocus = false;
43712         
43713         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43714             this.validate();
43715         }
43716         
43717         var v = this.getValue();
43718         
43719         if(String(v) !== String(this.startValue)){
43720             this.fireEvent('change', this, v, this.startValue);
43721         }
43722         
43723         this.fireEvent("blur", this);
43724     },
43725     
43726     inputEl : function()
43727     {
43728         return this.el.select('.roo-money-amount-input', true).first();
43729     },
43730     
43731     currencyEl : function()
43732     {
43733         return this.el.select('.roo-money-currency-input', true).first();
43734     },
43735     
43736     hiddenEl : function()
43737     {
43738         return this.el.select('input.hidden-number-input',true).first();
43739     }
43740     
43741 });/**
43742  * @class Roo.bootstrap.BezierSignature
43743  * @extends Roo.bootstrap.Component
43744  * Bootstrap BezierSignature class
43745  * This script refer to:
43746  *    Title: Signature Pad
43747  *    Author: szimek
43748  *    Availability: https://github.com/szimek/signature_pad
43749  *
43750  * @constructor
43751  * Create a new BezierSignature
43752  * @param {Object} config The config object
43753  */
43754
43755 Roo.bootstrap.BezierSignature = function(config){
43756     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43757     this.addEvents({
43758         "resize" : true
43759     });
43760 };
43761
43762 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43763 {
43764      
43765     curve_data: [],
43766     
43767     is_empty: true,
43768     
43769     mouse_btn_down: true,
43770     
43771     /**
43772      * @cfg {int} canvas height
43773      */
43774     canvas_height: '200px',
43775     
43776     /**
43777      * @cfg {float|function} Radius of a single dot.
43778      */ 
43779     dot_size: false,
43780     
43781     /**
43782      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43783      */
43784     min_width: 0.5,
43785     
43786     /**
43787      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43788      */
43789     max_width: 2.5,
43790     
43791     /**
43792      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43793      */
43794     throttle: 16,
43795     
43796     /**
43797      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43798      */
43799     min_distance: 5,
43800     
43801     /**
43802      * @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.
43803      */
43804     bg_color: 'rgba(0, 0, 0, 0)',
43805     
43806     /**
43807      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43808      */
43809     dot_color: 'black',
43810     
43811     /**
43812      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43813      */ 
43814     velocity_filter_weight: 0.7,
43815     
43816     /**
43817      * @cfg {function} Callback when stroke begin. 
43818      */
43819     onBegin: false,
43820     
43821     /**
43822      * @cfg {function} Callback when stroke end.
43823      */
43824     onEnd: false,
43825     
43826     getAutoCreate : function()
43827     {
43828         var cls = 'roo-signature column';
43829         
43830         if(this.cls){
43831             cls += ' ' + this.cls;
43832         }
43833         
43834         var col_sizes = [
43835             'lg',
43836             'md',
43837             'sm',
43838             'xs'
43839         ];
43840         
43841         for(var i = 0; i < col_sizes.length; i++) {
43842             if(this[col_sizes[i]]) {
43843                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43844             }
43845         }
43846         
43847         var cfg = {
43848             tag: 'div',
43849             cls: cls,
43850             cn: [
43851                 {
43852                     tag: 'div',
43853                     cls: 'roo-signature-body',
43854                     cn: [
43855                         {
43856                             tag: 'canvas',
43857                             cls: 'roo-signature-body-canvas',
43858                             height: this.canvas_height,
43859                             width: this.canvas_width
43860                         }
43861                     ]
43862                 },
43863                 {
43864                     tag: 'input',
43865                     type: 'file',
43866                     style: 'display: none'
43867                 }
43868             ]
43869         };
43870         
43871         return cfg;
43872     },
43873     
43874     initEvents: function() 
43875     {
43876         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43877         
43878         var canvas = this.canvasEl();
43879         
43880         // mouse && touch event swapping...
43881         canvas.dom.style.touchAction = 'none';
43882         canvas.dom.style.msTouchAction = 'none';
43883         
43884         this.mouse_btn_down = false;
43885         canvas.on('mousedown', this._handleMouseDown, this);
43886         canvas.on('mousemove', this._handleMouseMove, this);
43887         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43888         
43889         if (window.PointerEvent) {
43890             canvas.on('pointerdown', this._handleMouseDown, this);
43891             canvas.on('pointermove', this._handleMouseMove, this);
43892             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43893         }
43894         
43895         if ('ontouchstart' in window) {
43896             canvas.on('touchstart', this._handleTouchStart, this);
43897             canvas.on('touchmove', this._handleTouchMove, this);
43898             canvas.on('touchend', this._handleTouchEnd, this);
43899         }
43900         
43901         Roo.EventManager.onWindowResize(this.resize, this, true);
43902         
43903         // file input event
43904         this.fileEl().on('change', this.uploadImage, this);
43905         
43906         this.clear();
43907         
43908         this.resize();
43909     },
43910     
43911     resize: function(){
43912         
43913         var canvas = this.canvasEl().dom;
43914         var ctx = this.canvasElCtx();
43915         var img_data = false;
43916         
43917         if(canvas.width > 0) {
43918             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43919         }
43920         // setting canvas width will clean img data
43921         canvas.width = 0;
43922         
43923         var style = window.getComputedStyle ? 
43924             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43925             
43926         var padding_left = parseInt(style.paddingLeft) || 0;
43927         var padding_right = parseInt(style.paddingRight) || 0;
43928         
43929         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43930         
43931         if(img_data) {
43932             ctx.putImageData(img_data, 0, 0);
43933         }
43934     },
43935     
43936     _handleMouseDown: function(e)
43937     {
43938         if (e.browserEvent.which === 1) {
43939             this.mouse_btn_down = true;
43940             this.strokeBegin(e);
43941         }
43942     },
43943     
43944     _handleMouseMove: function (e)
43945     {
43946         if (this.mouse_btn_down) {
43947             this.strokeMoveUpdate(e);
43948         }
43949     },
43950     
43951     _handleMouseUp: function (e)
43952     {
43953         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43954             this.mouse_btn_down = false;
43955             this.strokeEnd(e);
43956         }
43957     },
43958     
43959     _handleTouchStart: function (e) {
43960         
43961         e.preventDefault();
43962         if (e.browserEvent.targetTouches.length === 1) {
43963             // var touch = e.browserEvent.changedTouches[0];
43964             // this.strokeBegin(touch);
43965             
43966              this.strokeBegin(e); // assume e catching the correct xy...
43967         }
43968     },
43969     
43970     _handleTouchMove: function (e) {
43971         e.preventDefault();
43972         // var touch = event.targetTouches[0];
43973         // _this._strokeMoveUpdate(touch);
43974         this.strokeMoveUpdate(e);
43975     },
43976     
43977     _handleTouchEnd: function (e) {
43978         var wasCanvasTouched = e.target === this.canvasEl().dom;
43979         if (wasCanvasTouched) {
43980             e.preventDefault();
43981             // var touch = event.changedTouches[0];
43982             // _this._strokeEnd(touch);
43983             this.strokeEnd(e);
43984         }
43985     },
43986     
43987     reset: function () {
43988         this._lastPoints = [];
43989         this._lastVelocity = 0;
43990         this._lastWidth = (this.min_width + this.max_width) / 2;
43991         this.canvasElCtx().fillStyle = this.dot_color;
43992     },
43993     
43994     strokeMoveUpdate: function(e)
43995     {
43996         this.strokeUpdate(e);
43997         
43998         if (this.throttle) {
43999             this.throttleStroke(this.strokeUpdate, this.throttle);
44000         }
44001         else {
44002             this.strokeUpdate(e);
44003         }
44004     },
44005     
44006     strokeBegin: function(e)
44007     {
44008         var newPointGroup = {
44009             color: this.dot_color,
44010             points: []
44011         };
44012         
44013         if (typeof this.onBegin === 'function') {
44014             this.onBegin(e);
44015         }
44016         
44017         this.curve_data.push(newPointGroup);
44018         this.reset();
44019         this.strokeUpdate(e);
44020     },
44021     
44022     strokeUpdate: function(e)
44023     {
44024         var rect = this.canvasEl().dom.getBoundingClientRect();
44025         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44026         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44027         var lastPoints = lastPointGroup.points;
44028         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44029         var isLastPointTooClose = lastPoint
44030             ? point.distanceTo(lastPoint) <= this.min_distance
44031             : false;
44032         var color = lastPointGroup.color;
44033         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44034             var curve = this.addPoint(point);
44035             if (!lastPoint) {
44036                 this.drawDot({color: color, point: point});
44037             }
44038             else if (curve) {
44039                 this.drawCurve({color: color, curve: curve});
44040             }
44041             lastPoints.push({
44042                 time: point.time,
44043                 x: point.x,
44044                 y: point.y
44045             });
44046         }
44047     },
44048     
44049     strokeEnd: function(e)
44050     {
44051         this.strokeUpdate(e);
44052         if (typeof this.onEnd === 'function') {
44053             this.onEnd(e);
44054         }
44055     },
44056     
44057     addPoint:  function (point) {
44058         var _lastPoints = this._lastPoints;
44059         _lastPoints.push(point);
44060         if (_lastPoints.length > 2) {
44061             if (_lastPoints.length === 3) {
44062                 _lastPoints.unshift(_lastPoints[0]);
44063             }
44064             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44065             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44066             _lastPoints.shift();
44067             return curve;
44068         }
44069         return null;
44070     },
44071     
44072     calculateCurveWidths: function (startPoint, endPoint) {
44073         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44074             (1 - this.velocity_filter_weight) * this._lastVelocity;
44075
44076         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44077         var widths = {
44078             end: newWidth,
44079             start: this._lastWidth
44080         };
44081         
44082         this._lastVelocity = velocity;
44083         this._lastWidth = newWidth;
44084         return widths;
44085     },
44086     
44087     drawDot: function (_a) {
44088         var color = _a.color, point = _a.point;
44089         var ctx = this.canvasElCtx();
44090         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44091         ctx.beginPath();
44092         this.drawCurveSegment(point.x, point.y, width);
44093         ctx.closePath();
44094         ctx.fillStyle = color;
44095         ctx.fill();
44096     },
44097     
44098     drawCurve: function (_a) {
44099         var color = _a.color, curve = _a.curve;
44100         var ctx = this.canvasElCtx();
44101         var widthDelta = curve.endWidth - curve.startWidth;
44102         var drawSteps = Math.floor(curve.length()) * 2;
44103         ctx.beginPath();
44104         ctx.fillStyle = color;
44105         for (var i = 0; i < drawSteps; i += 1) {
44106         var t = i / drawSteps;
44107         var tt = t * t;
44108         var ttt = tt * t;
44109         var u = 1 - t;
44110         var uu = u * u;
44111         var uuu = uu * u;
44112         var x = uuu * curve.startPoint.x;
44113         x += 3 * uu * t * curve.control1.x;
44114         x += 3 * u * tt * curve.control2.x;
44115         x += ttt * curve.endPoint.x;
44116         var y = uuu * curve.startPoint.y;
44117         y += 3 * uu * t * curve.control1.y;
44118         y += 3 * u * tt * curve.control2.y;
44119         y += ttt * curve.endPoint.y;
44120         var width = curve.startWidth + ttt * widthDelta;
44121         this.drawCurveSegment(x, y, width);
44122         }
44123         ctx.closePath();
44124         ctx.fill();
44125     },
44126     
44127     drawCurveSegment: function (x, y, width) {
44128         var ctx = this.canvasElCtx();
44129         ctx.moveTo(x, y);
44130         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44131         this.is_empty = false;
44132     },
44133     
44134     clear: function()
44135     {
44136         var ctx = this.canvasElCtx();
44137         var canvas = this.canvasEl().dom;
44138         ctx.fillStyle = this.bg_color;
44139         ctx.clearRect(0, 0, canvas.width, canvas.height);
44140         ctx.fillRect(0, 0, canvas.width, canvas.height);
44141         this.curve_data = [];
44142         this.reset();
44143         this.is_empty = true;
44144     },
44145     
44146     fileEl: function()
44147     {
44148         return  this.el.select('input',true).first();
44149     },
44150     
44151     canvasEl: function()
44152     {
44153         return this.el.select('canvas',true).first();
44154     },
44155     
44156     canvasElCtx: function()
44157     {
44158         return this.el.select('canvas',true).first().dom.getContext('2d');
44159     },
44160     
44161     getImage: function(type)
44162     {
44163         if(this.is_empty) {
44164             return false;
44165         }
44166         
44167         // encryption ?
44168         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44169     },
44170     
44171     drawFromImage: function(img_src)
44172     {
44173         var img = new Image();
44174         
44175         img.onload = function(){
44176             this.canvasElCtx().drawImage(img, 0, 0);
44177         }.bind(this);
44178         
44179         img.src = img_src;
44180         
44181         this.is_empty = false;
44182     },
44183     
44184     selectImage: function()
44185     {
44186         this.fileEl().dom.click();
44187     },
44188     
44189     uploadImage: function(e)
44190     {
44191         var reader = new FileReader();
44192         
44193         reader.onload = function(e){
44194             var img = new Image();
44195             img.onload = function(){
44196                 this.reset();
44197                 this.canvasElCtx().drawImage(img, 0, 0);
44198             }.bind(this);
44199             img.src = e.target.result;
44200         }.bind(this);
44201         
44202         reader.readAsDataURL(e.target.files[0]);
44203     },
44204     
44205     // Bezier Point Constructor
44206     Point: (function () {
44207         function Point(x, y, time) {
44208             this.x = x;
44209             this.y = y;
44210             this.time = time || Date.now();
44211         }
44212         Point.prototype.distanceTo = function (start) {
44213             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44214         };
44215         Point.prototype.equals = function (other) {
44216             return this.x === other.x && this.y === other.y && this.time === other.time;
44217         };
44218         Point.prototype.velocityFrom = function (start) {
44219             return this.time !== start.time
44220             ? this.distanceTo(start) / (this.time - start.time)
44221             : 0;
44222         };
44223         return Point;
44224     }()),
44225     
44226     
44227     // Bezier Constructor
44228     Bezier: (function () {
44229         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44230             this.startPoint = startPoint;
44231             this.control2 = control2;
44232             this.control1 = control1;
44233             this.endPoint = endPoint;
44234             this.startWidth = startWidth;
44235             this.endWidth = endWidth;
44236         }
44237         Bezier.fromPoints = function (points, widths, scope) {
44238             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44239             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44240             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44241         };
44242         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44243             var dx1 = s1.x - s2.x;
44244             var dy1 = s1.y - s2.y;
44245             var dx2 = s2.x - s3.x;
44246             var dy2 = s2.y - s3.y;
44247             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44248             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44249             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44250             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44251             var dxm = m1.x - m2.x;
44252             var dym = m1.y - m2.y;
44253             var k = l2 / (l1 + l2);
44254             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44255             var tx = s2.x - cm.x;
44256             var ty = s2.y - cm.y;
44257             return {
44258                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44259                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44260             };
44261         };
44262         Bezier.prototype.length = function () {
44263             var steps = 10;
44264             var length = 0;
44265             var px;
44266             var py;
44267             for (var i = 0; i <= steps; i += 1) {
44268                 var t = i / steps;
44269                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44270                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44271                 if (i > 0) {
44272                     var xdiff = cx - px;
44273                     var ydiff = cy - py;
44274                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44275                 }
44276                 px = cx;
44277                 py = cy;
44278             }
44279             return length;
44280         };
44281         Bezier.prototype.point = function (t, start, c1, c2, end) {
44282             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44283             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44284             + (3.0 * c2 * (1.0 - t) * t * t)
44285             + (end * t * t * t);
44286         };
44287         return Bezier;
44288     }()),
44289     
44290     throttleStroke: function(fn, wait) {
44291       if (wait === void 0) { wait = 250; }
44292       var previous = 0;
44293       var timeout = null;
44294       var result;
44295       var storedContext;
44296       var storedArgs;
44297       var later = function () {
44298           previous = Date.now();
44299           timeout = null;
44300           result = fn.apply(storedContext, storedArgs);
44301           if (!timeout) {
44302               storedContext = null;
44303               storedArgs = [];
44304           }
44305       };
44306       return function wrapper() {
44307           var args = [];
44308           for (var _i = 0; _i < arguments.length; _i++) {
44309               args[_i] = arguments[_i];
44310           }
44311           var now = Date.now();
44312           var remaining = wait - (now - previous);
44313           storedContext = this;
44314           storedArgs = args;
44315           if (remaining <= 0 || remaining > wait) {
44316               if (timeout) {
44317                   clearTimeout(timeout);
44318                   timeout = null;
44319               }
44320               previous = now;
44321               result = fn.apply(storedContext, storedArgs);
44322               if (!timeout) {
44323                   storedContext = null;
44324                   storedArgs = [];
44325               }
44326           }
44327           else if (!timeout) {
44328               timeout = window.setTimeout(later, remaining);
44329           }
44330           return result;
44331       };
44332   }
44333   
44334 });
44335
44336  
44337
44338