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 {Boolean} button_outline show and outlined button
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6109         
6110         if (this.active) {
6111             cfg.cls +=  ' active' ;
6112         }
6113         if (this.disabled) {
6114             cfg.cls += ' disabled';
6115         }
6116         
6117         // BS4 only?
6118         if (this.button_weight.length) {
6119             cfg.tag = this.href ? 'a' : 'button';
6120             cfg.html = this.html || '';
6121             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6122             if (this.href) {
6123                 cfg.href = this.href;
6124             }
6125             if (this.fa) {
6126                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6127             }
6128             
6129             // menu .. should add dropdown-menu class - so no need for carat..
6130             
6131             if (this.badge !== '') {
6132                  
6133                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6134             }
6135             return cfg;
6136         }
6137         
6138         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6139             cfg.cn = [
6140                 {
6141                     tag: this.tagtype,
6142                     href : this.href || "#",
6143                     html: this.html || ''
6144                 }
6145             ];
6146             if (this.tagtype == 'a') {
6147                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '');
6148         
6149             }
6150             if (this.icon) {
6151                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if (this.fa) {
6154                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6155             }
6156             if(this.glyphicon) {
6157                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6158             }
6159             
6160             if (this.menu) {
6161                 
6162                 cfg.cn[0].html += " <span class='caret'></span>";
6163              
6164             }
6165             
6166             if (this.badge !== '') {
6167                  
6168                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6169             }
6170         }
6171         
6172         
6173         
6174         return cfg;
6175     },
6176     onRender : function(ct, position)
6177     {
6178        // Roo.log("Call onRender: " + this.xtype);
6179         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6180             this.tag = 'div';
6181         }
6182         
6183         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6184         this.navLink = this.el.select('.nav-link',true).first();
6185         return ret;
6186     },
6187       
6188     
6189     initEvents: function() 
6190     {
6191         if (typeof (this.menu) != 'undefined') {
6192             this.menu.parentType = this.xtype;
6193             this.menu.triggerEl = this.el;
6194             this.menu = this.addxtype(Roo.apply({}, this.menu));
6195         }
6196         
6197         this.el.select('a',true).on('click', this.onClick, this);
6198         
6199         if(this.tagtype == 'span'){
6200             this.el.select('span',true).on('click', this.onClick, this);
6201         }
6202        
6203         // at this point parent should be available..
6204         this.parent().register(this);
6205     },
6206     
6207     onClick : function(e)
6208     {
6209         if (e.getTarget('.dropdown-menu-item')) {
6210             // did you click on a menu itemm.... - then don't trigger onclick..
6211             return;
6212         }
6213         
6214         if(
6215                 this.preventDefault || 
6216                 this.href == '#' 
6217         ){
6218             Roo.log("NavItem - prevent Default?");
6219             e.preventDefault();
6220         }
6221         
6222         if (this.disabled) {
6223             return;
6224         }
6225         
6226         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6227         if (tg && tg.transition) {
6228             Roo.log("waiting for the transitionend");
6229             return;
6230         }
6231         
6232         
6233         
6234         //Roo.log("fire event clicked");
6235         if(this.fireEvent('click', this, e) === false){
6236             return;
6237         };
6238         
6239         if(this.tagtype == 'span'){
6240             return;
6241         }
6242         
6243         //Roo.log(this.href);
6244         var ael = this.el.select('a',true).first();
6245         //Roo.log(ael);
6246         
6247         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6248             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6249             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6250                 return; // ignore... - it's a 'hash' to another page.
6251             }
6252             Roo.log("NavItem - prevent Default?");
6253             e.preventDefault();
6254             this.scrollToElement(e);
6255         }
6256         
6257         
6258         var p =  this.parent();
6259    
6260         if (['tabs','pills'].indexOf(p.type)!==-1) {
6261             if (typeof(p.setActiveItem) !== 'undefined') {
6262                 p.setActiveItem(this);
6263             }
6264         }
6265         
6266         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6267         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6268             // remove the collapsed menu expand...
6269             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6270         }
6271     },
6272     
6273     isActive: function () {
6274         return this.active
6275     },
6276     setActive : function(state, fire, is_was_active)
6277     {
6278         if (this.active && !state && this.navId) {
6279             this.was_active = true;
6280             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6281             if (nv) {
6282                 nv.clearWasActive(this);
6283             }
6284             
6285         }
6286         this.active = state;
6287         
6288         if (!state ) {
6289             this.el.removeClass('active');
6290             this.navLink ? this.navLink.removeClass('active') : false;
6291         } else if (!this.el.hasClass('active')) {
6292             
6293             this.el.addClass('active');
6294             if (Roo.bootstrap.version == 4 && this.navLink ) {
6295                 this.navLink.addClass('active');
6296             }
6297             
6298         }
6299         if (fire) {
6300             this.fireEvent('changed', this, state);
6301         }
6302         
6303         // show a panel if it's registered and related..
6304         
6305         if (!this.navId || !this.tabId || !state || is_was_active) {
6306             return;
6307         }
6308         
6309         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6310         if (!tg) {
6311             return;
6312         }
6313         var pan = tg.getPanelByName(this.tabId);
6314         if (!pan) {
6315             return;
6316         }
6317         // if we can not flip to new panel - go back to old nav highlight..
6318         if (false == tg.showPanel(pan)) {
6319             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6320             if (nv) {
6321                 var onav = nv.getWasActive();
6322                 if (onav) {
6323                     onav.setActive(true, false, true);
6324                 }
6325             }
6326             
6327         }
6328         
6329         
6330         
6331     },
6332      // this should not be here...
6333     setDisabled : function(state)
6334     {
6335         this.disabled = state;
6336         if (!state ) {
6337             this.el.removeClass('disabled');
6338         } else if (!this.el.hasClass('disabled')) {
6339             this.el.addClass('disabled');
6340         }
6341         
6342     },
6343     
6344     /**
6345      * Fetch the element to display the tooltip on.
6346      * @return {Roo.Element} defaults to this.el
6347      */
6348     tooltipEl : function()
6349     {
6350         return this.el.select('' + this.tagtype + '', true).first();
6351     },
6352     
6353     scrollToElement : function(e)
6354     {
6355         var c = document.body;
6356         
6357         /*
6358          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6359          */
6360         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6361             c = document.documentElement;
6362         }
6363         
6364         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6365         
6366         if(!target){
6367             return;
6368         }
6369
6370         var o = target.calcOffsetsTo(c);
6371         
6372         var options = {
6373             target : target,
6374             value : o[1]
6375         };
6376         
6377         this.fireEvent('scrollto', this, options, e);
6378         
6379         Roo.get(c).scrollTo('top', options.value, true);
6380         
6381         return;
6382     }
6383 });
6384  
6385
6386  /*
6387  * - LGPL
6388  *
6389  * sidebar item
6390  *
6391  *  li
6392  *    <span> icon </span>
6393  *    <span> text </span>
6394  *    <span>badge </span>
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.NavSidebarItem
6399  * @extends Roo.bootstrap.NavItem
6400  * Bootstrap Navbar.NavSidebarItem class
6401  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6402  * {Boolean} open is the menu open
6403  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6404  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6405  * {String} buttonSize (sm|md|lg)the extra classes for the button
6406  * {Boolean} showArrow show arrow next to the text (default true)
6407  * @constructor
6408  * Create a new Navbar Button
6409  * @param {Object} config The config object
6410  */
6411 Roo.bootstrap.NavSidebarItem = function(config){
6412     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6413     this.addEvents({
6414         // raw events
6415         /**
6416          * @event click
6417          * The raw click event for the entire grid.
6418          * @param {Roo.EventObject} e
6419          */
6420         "click" : true,
6421          /**
6422             * @event changed
6423             * Fires when the active item active state changes
6424             * @param {Roo.bootstrap.NavSidebarItem} this
6425             * @param {boolean} state the new state
6426              
6427          */
6428         'changed': true
6429     });
6430    
6431 };
6432
6433 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6434     
6435     badgeWeight : 'default',
6436     
6437     open: false,
6438     
6439     buttonView : false,
6440     
6441     buttonWeight : 'default',
6442     
6443     buttonSize : 'md',
6444     
6445     showArrow : true,
6446     
6447     getAutoCreate : function(){
6448         
6449         
6450         var a = {
6451                 tag: 'a',
6452                 href : this.href || '#',
6453                 cls: '',
6454                 html : '',
6455                 cn : []
6456         };
6457         
6458         if(this.buttonView){
6459             a = {
6460                 tag: 'button',
6461                 href : this.href || '#',
6462                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6463                 html : this.html,
6464                 cn : []
6465             };
6466         }
6467         
6468         var cfg = {
6469             tag: 'li',
6470             cls: '',
6471             cn: [ a ]
6472         };
6473         
6474         if (this.active) {
6475             cfg.cls += ' active';
6476         }
6477         
6478         if (this.disabled) {
6479             cfg.cls += ' disabled';
6480         }
6481         if (this.open) {
6482             cfg.cls += ' open x-open';
6483         }
6484         // left icon..
6485         if (this.glyphicon || this.icon) {
6486             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6487             a.cn.push({ tag : 'i', cls : c }) ;
6488         }
6489         
6490         if(!this.buttonView){
6491             var span = {
6492                 tag: 'span',
6493                 html : this.html || ''
6494             };
6495
6496             a.cn.push(span);
6497             
6498         }
6499         
6500         if (this.badge !== '') {
6501             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6502         }
6503         
6504         if (this.menu) {
6505             
6506             if(this.showArrow){
6507                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6508             }
6509             
6510             a.cls += ' dropdown-toggle treeview' ;
6511         }
6512         
6513         return cfg;
6514     },
6515     
6516     initEvents : function()
6517     { 
6518         if (typeof (this.menu) != 'undefined') {
6519             this.menu.parentType = this.xtype;
6520             this.menu.triggerEl = this.el;
6521             this.menu = this.addxtype(Roo.apply({}, this.menu));
6522         }
6523         
6524         this.el.on('click', this.onClick, this);
6525         
6526         if(this.badge !== ''){
6527             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6528         }
6529         
6530     },
6531     
6532     onClick : function(e)
6533     {
6534         if(this.disabled){
6535             e.preventDefault();
6536             return;
6537         }
6538         
6539         if(this.preventDefault){
6540             e.preventDefault();
6541         }
6542         
6543         this.fireEvent('click', this, e);
6544     },
6545     
6546     disable : function()
6547     {
6548         this.setDisabled(true);
6549     },
6550     
6551     enable : function()
6552     {
6553         this.setDisabled(false);
6554     },
6555     
6556     setDisabled : function(state)
6557     {
6558         if(this.disabled == state){
6559             return;
6560         }
6561         
6562         this.disabled = state;
6563         
6564         if (state) {
6565             this.el.addClass('disabled');
6566             return;
6567         }
6568         
6569         this.el.removeClass('disabled');
6570         
6571         return;
6572     },
6573     
6574     setActive : function(state)
6575     {
6576         if(this.active == state){
6577             return;
6578         }
6579         
6580         this.active = state;
6581         
6582         if (state) {
6583             this.el.addClass('active');
6584             return;
6585         }
6586         
6587         this.el.removeClass('active');
6588         
6589         return;
6590     },
6591     
6592     isActive: function () 
6593     {
6594         return this.active;
6595     },
6596     
6597     setBadge : function(str)
6598     {
6599         if(!this.badgeEl){
6600             return;
6601         }
6602         
6603         this.badgeEl.dom.innerHTML = str;
6604     }
6605     
6606    
6607      
6608  
6609 });
6610  
6611
6612  /*
6613  * - LGPL
6614  *
6615  *  Breadcrumb Nav
6616  * 
6617  */
6618 Roo.namespace('Roo.bootstrap.breadcrumb');
6619
6620
6621 /**
6622  * @class Roo.bootstrap.breadcrumb.Nav
6623  * @extends Roo.bootstrap.Component
6624  * Bootstrap Breadcrumb Nav Class
6625  *  
6626  * @children Roo.bootstrap.breadcrumb.Item
6627  * 
6628  * @constructor
6629  * Create a new breadcrumb.Nav
6630  * @param {Object} config The config object
6631  */
6632
6633
6634 Roo.bootstrap.breadcrumb.Nav = function(config){
6635     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6636     
6637     
6638 };
6639
6640 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6641     
6642     getAutoCreate : function()
6643     {
6644
6645         var cfg = {
6646             tag: 'nav',
6647             cn : [
6648                 {
6649                     tag : 'ol',
6650                     cls : 'breadcrumb'
6651                 }
6652             ]
6653             
6654         };
6655           
6656         return cfg;
6657     },
6658     
6659     initEvents: function()
6660     {
6661         this.olEl = this.el.select('ol',true).first();    
6662     },
6663     getChildContainer : function()
6664     {
6665         return this.olEl;  
6666     }
6667     
6668 });
6669
6670  /*
6671  * - LGPL
6672  *
6673  *  Breadcrumb Item
6674  * 
6675  */
6676
6677
6678 /**
6679  * @class Roo.bootstrap.breadcrumb.Nav
6680  * @extends Roo.bootstrap.Component
6681  * Bootstrap Breadcrumb Nav Class
6682  *  
6683  * @children Roo.bootstrap.breadcrumb.Component
6684  * @cfg {String} html the content of the link.
6685  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6686  * @cfg {Boolean} active is it active
6687
6688  * 
6689  * @constructor
6690  * Create a new breadcrumb.Nav
6691  * @param {Object} config The config object
6692  */
6693
6694 Roo.bootstrap.breadcrumb.Item = function(config){
6695     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6696     this.addEvents({
6697         // img events
6698         /**
6699          * @event click
6700          * The img click event for the img.
6701          * @param {Roo.EventObject} e
6702          */
6703         "click" : true
6704     });
6705     
6706 };
6707
6708 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6709     
6710     href: false,
6711     html : '',
6712     
6713     getAutoCreate : function()
6714     {
6715
6716         var cfg = {
6717             tag: 'li',
6718             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6719         };
6720         if (this.href !== false) {
6721             cfg.cn = [{
6722                 tag : 'a',
6723                 href : this.href,
6724                 html : this.html
6725             }];
6726         } else {
6727             cfg.html = this.html;
6728         }
6729         
6730         return cfg;
6731     },
6732     
6733     initEvents: function()
6734     {
6735         if (this.href) {
6736             this.el.select('a', true).first().on('click',this.onClick, this)
6737         }
6738         
6739     },
6740     onClick : function(e)
6741     {
6742         e.preventDefault();
6743         this.fireEvent('click',this,  e);
6744     }
6745     
6746 });
6747
6748  /*
6749  * - LGPL
6750  *
6751  * row
6752  * 
6753  */
6754
6755 /**
6756  * @class Roo.bootstrap.Row
6757  * @extends Roo.bootstrap.Component
6758  * Bootstrap Row class (contains columns...)
6759  * 
6760  * @constructor
6761  * Create a new Row
6762  * @param {Object} config The config object
6763  */
6764
6765 Roo.bootstrap.Row = function(config){
6766     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6767 };
6768
6769 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6770     
6771     getAutoCreate : function(){
6772        return {
6773             cls: 'row clearfix'
6774        };
6775     }
6776     
6777     
6778 });
6779
6780  
6781
6782  /*
6783  * - LGPL
6784  *
6785  * pagination
6786  * 
6787  */
6788
6789 /**
6790  * @class Roo.bootstrap.Pagination
6791  * @extends Roo.bootstrap.Component
6792  * Bootstrap Pagination class
6793  * @cfg {String} size xs | sm | md | lg
6794  * @cfg {Boolean} inverse false | true
6795  * 
6796  * @constructor
6797  * Create a new Pagination
6798  * @param {Object} config The config object
6799  */
6800
6801 Roo.bootstrap.Pagination = function(config){
6802     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6803 };
6804
6805 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6806     
6807     cls: false,
6808     size: false,
6809     inverse: false,
6810     
6811     getAutoCreate : function(){
6812         var cfg = {
6813             tag: 'ul',
6814                 cls: 'pagination'
6815         };
6816         if (this.inverse) {
6817             cfg.cls += ' inverse';
6818         }
6819         if (this.html) {
6820             cfg.html=this.html;
6821         }
6822         if (this.cls) {
6823             cfg.cls += " " + this.cls;
6824         }
6825         return cfg;
6826     }
6827    
6828 });
6829
6830  
6831
6832  /*
6833  * - LGPL
6834  *
6835  * Pagination item
6836  * 
6837  */
6838
6839
6840 /**
6841  * @class Roo.bootstrap.PaginationItem
6842  * @extends Roo.bootstrap.Component
6843  * Bootstrap PaginationItem class
6844  * @cfg {String} html text
6845  * @cfg {String} href the link
6846  * @cfg {Boolean} preventDefault (true | false) default true
6847  * @cfg {Boolean} active (true | false) default false
6848  * @cfg {Boolean} disabled default false
6849  * 
6850  * 
6851  * @constructor
6852  * Create a new PaginationItem
6853  * @param {Object} config The config object
6854  */
6855
6856
6857 Roo.bootstrap.PaginationItem = function(config){
6858     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6859     this.addEvents({
6860         // raw events
6861         /**
6862          * @event click
6863          * The raw click event for the entire grid.
6864          * @param {Roo.EventObject} e
6865          */
6866         "click" : true
6867     });
6868 };
6869
6870 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6871     
6872     href : false,
6873     html : false,
6874     preventDefault: true,
6875     active : false,
6876     cls : false,
6877     disabled: false,
6878     
6879     getAutoCreate : function(){
6880         var cfg= {
6881             tag: 'li',
6882             cn: [
6883                 {
6884                     tag : 'a',
6885                     href : this.href ? this.href : '#',
6886                     html : this.html ? this.html : ''
6887                 }
6888             ]
6889         };
6890         
6891         if(this.cls){
6892             cfg.cls = this.cls;
6893         }
6894         
6895         if(this.disabled){
6896             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6897         }
6898         
6899         if(this.active){
6900             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6901         }
6902         
6903         return cfg;
6904     },
6905     
6906     initEvents: function() {
6907         
6908         this.el.on('click', this.onClick, this);
6909         
6910     },
6911     onClick : function(e)
6912     {
6913         Roo.log('PaginationItem on click ');
6914         if(this.preventDefault){
6915             e.preventDefault();
6916         }
6917         
6918         if(this.disabled){
6919             return;
6920         }
6921         
6922         this.fireEvent('click', this, e);
6923     }
6924    
6925 });
6926
6927  
6928
6929  /*
6930  * - LGPL
6931  *
6932  * slider
6933  * 
6934  */
6935
6936
6937 /**
6938  * @class Roo.bootstrap.Slider
6939  * @extends Roo.bootstrap.Component
6940  * Bootstrap Slider class
6941  *    
6942  * @constructor
6943  * Create a new Slider
6944  * @param {Object} config The config object
6945  */
6946
6947 Roo.bootstrap.Slider = function(config){
6948     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6949 };
6950
6951 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6952     
6953     getAutoCreate : function(){
6954         
6955         var cfg = {
6956             tag: 'div',
6957             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6958             cn: [
6959                 {
6960                     tag: 'a',
6961                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6962                 }
6963             ]
6964         };
6965         
6966         return cfg;
6967     }
6968    
6969 });
6970
6971  /*
6972  * Based on:
6973  * Ext JS Library 1.1.1
6974  * Copyright(c) 2006-2007, Ext JS, LLC.
6975  *
6976  * Originally Released Under LGPL - original licence link has changed is not relivant.
6977  *
6978  * Fork - LGPL
6979  * <script type="text/javascript">
6980  */
6981  
6982
6983 /**
6984  * @class Roo.grid.ColumnModel
6985  * @extends Roo.util.Observable
6986  * This is the default implementation of a ColumnModel used by the Grid. It defines
6987  * the columns in the grid.
6988  * <br>Usage:<br>
6989  <pre><code>
6990  var colModel = new Roo.grid.ColumnModel([
6991         {header: "Ticker", width: 60, sortable: true, locked: true},
6992         {header: "Company Name", width: 150, sortable: true},
6993         {header: "Market Cap.", width: 100, sortable: true},
6994         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6995         {header: "Employees", width: 100, sortable: true, resizable: false}
6996  ]);
6997  </code></pre>
6998  * <p>
6999  
7000  * The config options listed for this class are options which may appear in each
7001  * individual column definition.
7002  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7003  * @constructor
7004  * @param {Object} config An Array of column config objects. See this class's
7005  * config objects for details.
7006 */
7007 Roo.grid.ColumnModel = function(config){
7008         /**
7009      * The config passed into the constructor
7010      */
7011     this.config = config;
7012     this.lookup = {};
7013
7014     // if no id, create one
7015     // if the column does not have a dataIndex mapping,
7016     // map it to the order it is in the config
7017     for(var i = 0, len = config.length; i < len; i++){
7018         var c = config[i];
7019         if(typeof c.dataIndex == "undefined"){
7020             c.dataIndex = i;
7021         }
7022         if(typeof c.renderer == "string"){
7023             c.renderer = Roo.util.Format[c.renderer];
7024         }
7025         if(typeof c.id == "undefined"){
7026             c.id = Roo.id();
7027         }
7028         if(c.editor && c.editor.xtype){
7029             c.editor  = Roo.factory(c.editor, Roo.grid);
7030         }
7031         if(c.editor && c.editor.isFormField){
7032             c.editor = new Roo.grid.GridEditor(c.editor);
7033         }
7034         this.lookup[c.id] = c;
7035     }
7036
7037     /**
7038      * The width of columns which have no width specified (defaults to 100)
7039      * @type Number
7040      */
7041     this.defaultWidth = 100;
7042
7043     /**
7044      * Default sortable of columns which have no sortable specified (defaults to false)
7045      * @type Boolean
7046      */
7047     this.defaultSortable = false;
7048
7049     this.addEvents({
7050         /**
7051              * @event widthchange
7052              * Fires when the width of a column changes.
7053              * @param {ColumnModel} this
7054              * @param {Number} columnIndex The column index
7055              * @param {Number} newWidth The new width
7056              */
7057             "widthchange": true,
7058         /**
7059              * @event headerchange
7060              * Fires when the text of a header changes.
7061              * @param {ColumnModel} this
7062              * @param {Number} columnIndex The column index
7063              * @param {Number} newText The new header text
7064              */
7065             "headerchange": true,
7066         /**
7067              * @event hiddenchange
7068              * Fires when a column is hidden or "unhidden".
7069              * @param {ColumnModel} this
7070              * @param {Number} columnIndex The column index
7071              * @param {Boolean} hidden true if hidden, false otherwise
7072              */
7073             "hiddenchange": true,
7074             /**
7075          * @event columnmoved
7076          * Fires when a column is moved.
7077          * @param {ColumnModel} this
7078          * @param {Number} oldIndex
7079          * @param {Number} newIndex
7080          */
7081         "columnmoved" : true,
7082         /**
7083          * @event columlockchange
7084          * Fires when a column's locked state is changed
7085          * @param {ColumnModel} this
7086          * @param {Number} colIndex
7087          * @param {Boolean} locked true if locked
7088          */
7089         "columnlockchange" : true
7090     });
7091     Roo.grid.ColumnModel.superclass.constructor.call(this);
7092 };
7093 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7094     /**
7095      * @cfg {String} header The header text to display in the Grid view.
7096      */
7097     /**
7098      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7099      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7100      * specified, the column's index is used as an index into the Record's data Array.
7101      */
7102     /**
7103      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7104      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7105      */
7106     /**
7107      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7108      * Defaults to the value of the {@link #defaultSortable} property.
7109      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7110      */
7111     /**
7112      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7116      */
7117     /**
7118      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7119      */
7120     /**
7121      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7122      */
7123     /**
7124      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7125      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7126      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7127      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7128      */
7129        /**
7130      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7131      */
7132     /**
7133      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7137      */
7138     /**
7139      * @cfg {String} cursor (Optional)
7140      */
7141     /**
7142      * @cfg {String} tooltip (Optional)
7143      */
7144     /**
7145      * @cfg {Number} xs (Optional)
7146      */
7147     /**
7148      * @cfg {Number} sm (Optional)
7149      */
7150     /**
7151      * @cfg {Number} md (Optional)
7152      */
7153     /**
7154      * @cfg {Number} lg (Optional)
7155      */
7156     /**
7157      * Returns the id of the column at the specified index.
7158      * @param {Number} index The column index
7159      * @return {String} the id
7160      */
7161     getColumnId : function(index){
7162         return this.config[index].id;
7163     },
7164
7165     /**
7166      * Returns the column for a specified id.
7167      * @param {String} id The column id
7168      * @return {Object} the column
7169      */
7170     getColumnById : function(id){
7171         return this.lookup[id];
7172     },
7173
7174     
7175     /**
7176      * Returns the column for a specified dataIndex.
7177      * @param {String} dataIndex The column dataIndex
7178      * @return {Object|Boolean} the column or false if not found
7179      */
7180     getColumnByDataIndex: function(dataIndex){
7181         var index = this.findColumnIndex(dataIndex);
7182         return index > -1 ? this.config[index] : false;
7183     },
7184     
7185     /**
7186      * Returns the index for a specified column id.
7187      * @param {String} id The column id
7188      * @return {Number} the index, or -1 if not found
7189      */
7190     getIndexById : function(id){
7191         for(var i = 0, len = this.config.length; i < len; i++){
7192             if(this.config[i].id == id){
7193                 return i;
7194             }
7195         }
7196         return -1;
7197     },
7198     
7199     /**
7200      * Returns the index for a specified column dataIndex.
7201      * @param {String} dataIndex The column dataIndex
7202      * @return {Number} the index, or -1 if not found
7203      */
7204     
7205     findColumnIndex : function(dataIndex){
7206         for(var i = 0, len = this.config.length; i < len; i++){
7207             if(this.config[i].dataIndex == dataIndex){
7208                 return i;
7209             }
7210         }
7211         return -1;
7212     },
7213     
7214     
7215     moveColumn : function(oldIndex, newIndex){
7216         var c = this.config[oldIndex];
7217         this.config.splice(oldIndex, 1);
7218         this.config.splice(newIndex, 0, c);
7219         this.dataMap = null;
7220         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7221     },
7222
7223     isLocked : function(colIndex){
7224         return this.config[colIndex].locked === true;
7225     },
7226
7227     setLocked : function(colIndex, value, suppressEvent){
7228         if(this.isLocked(colIndex) == value){
7229             return;
7230         }
7231         this.config[colIndex].locked = value;
7232         if(!suppressEvent){
7233             this.fireEvent("columnlockchange", this, colIndex, value);
7234         }
7235     },
7236
7237     getTotalLockedWidth : function(){
7238         var totalWidth = 0;
7239         for(var i = 0; i < this.config.length; i++){
7240             if(this.isLocked(i) && !this.isHidden(i)){
7241                 this.totalWidth += this.getColumnWidth(i);
7242             }
7243         }
7244         return totalWidth;
7245     },
7246
7247     getLockedCount : function(){
7248         for(var i = 0, len = this.config.length; i < len; i++){
7249             if(!this.isLocked(i)){
7250                 return i;
7251             }
7252         }
7253         
7254         return this.config.length;
7255     },
7256
7257     /**
7258      * Returns the number of columns.
7259      * @return {Number}
7260      */
7261     getColumnCount : function(visibleOnly){
7262         if(visibleOnly === true){
7263             var c = 0;
7264             for(var i = 0, len = this.config.length; i < len; i++){
7265                 if(!this.isHidden(i)){
7266                     c++;
7267                 }
7268             }
7269             return c;
7270         }
7271         return this.config.length;
7272     },
7273
7274     /**
7275      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7276      * @param {Function} fn
7277      * @param {Object} scope (optional)
7278      * @return {Array} result
7279      */
7280     getColumnsBy : function(fn, scope){
7281         var r = [];
7282         for(var i = 0, len = this.config.length; i < len; i++){
7283             var c = this.config[i];
7284             if(fn.call(scope||this, c, i) === true){
7285                 r[r.length] = c;
7286             }
7287         }
7288         return r;
7289     },
7290
7291     /**
7292      * Returns true if the specified column is sortable.
7293      * @param {Number} col The column index
7294      * @return {Boolean}
7295      */
7296     isSortable : function(col){
7297         if(typeof this.config[col].sortable == "undefined"){
7298             return this.defaultSortable;
7299         }
7300         return this.config[col].sortable;
7301     },
7302
7303     /**
7304      * Returns the rendering (formatting) function defined for the column.
7305      * @param {Number} col The column index.
7306      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7307      */
7308     getRenderer : function(col){
7309         if(!this.config[col].renderer){
7310             return Roo.grid.ColumnModel.defaultRenderer;
7311         }
7312         return this.config[col].renderer;
7313     },
7314
7315     /**
7316      * Sets the rendering (formatting) function for a column.
7317      * @param {Number} col The column index
7318      * @param {Function} fn The function to use to process the cell's raw data
7319      * to return HTML markup for the grid view. The render function is called with
7320      * the following parameters:<ul>
7321      * <li>Data value.</li>
7322      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7323      * <li>css A CSS style string to apply to the table cell.</li>
7324      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7325      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7326      * <li>Row index</li>
7327      * <li>Column index</li>
7328      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7329      */
7330     setRenderer : function(col, fn){
7331         this.config[col].renderer = fn;
7332     },
7333
7334     /**
7335      * Returns the width for the specified column.
7336      * @param {Number} col The column index
7337      * @return {Number}
7338      */
7339     getColumnWidth : function(col){
7340         return this.config[col].width * 1 || this.defaultWidth;
7341     },
7342
7343     /**
7344      * Sets the width for a column.
7345      * @param {Number} col The column index
7346      * @param {Number} width The new width
7347      */
7348     setColumnWidth : function(col, width, suppressEvent){
7349         this.config[col].width = width;
7350         this.totalWidth = null;
7351         if(!suppressEvent){
7352              this.fireEvent("widthchange", this, col, width);
7353         }
7354     },
7355
7356     /**
7357      * Returns the total width of all columns.
7358      * @param {Boolean} includeHidden True to include hidden column widths
7359      * @return {Number}
7360      */
7361     getTotalWidth : function(includeHidden){
7362         if(!this.totalWidth){
7363             this.totalWidth = 0;
7364             for(var i = 0, len = this.config.length; i < len; i++){
7365                 if(includeHidden || !this.isHidden(i)){
7366                     this.totalWidth += this.getColumnWidth(i);
7367                 }
7368             }
7369         }
7370         return this.totalWidth;
7371     },
7372
7373     /**
7374      * Returns the header for the specified column.
7375      * @param {Number} col The column index
7376      * @return {String}
7377      */
7378     getColumnHeader : function(col){
7379         return this.config[col].header;
7380     },
7381
7382     /**
7383      * Sets the header for a column.
7384      * @param {Number} col The column index
7385      * @param {String} header The new header
7386      */
7387     setColumnHeader : function(col, header){
7388         this.config[col].header = header;
7389         this.fireEvent("headerchange", this, col, header);
7390     },
7391
7392     /**
7393      * Returns the tooltip for the specified column.
7394      * @param {Number} col The column index
7395      * @return {String}
7396      */
7397     getColumnTooltip : function(col){
7398             return this.config[col].tooltip;
7399     },
7400     /**
7401      * Sets the tooltip for a column.
7402      * @param {Number} col The column index
7403      * @param {String} tooltip The new tooltip
7404      */
7405     setColumnTooltip : function(col, tooltip){
7406             this.config[col].tooltip = tooltip;
7407     },
7408
7409     /**
7410      * Returns the dataIndex for the specified column.
7411      * @param {Number} col The column index
7412      * @return {Number}
7413      */
7414     getDataIndex : function(col){
7415         return this.config[col].dataIndex;
7416     },
7417
7418     /**
7419      * Sets the dataIndex for a column.
7420      * @param {Number} col The column index
7421      * @param {Number} dataIndex The new dataIndex
7422      */
7423     setDataIndex : function(col, dataIndex){
7424         this.config[col].dataIndex = dataIndex;
7425     },
7426
7427     
7428     
7429     /**
7430      * Returns true if the cell is editable.
7431      * @param {Number} colIndex The column index
7432      * @param {Number} rowIndex The row index - this is nto actually used..?
7433      * @return {Boolean}
7434      */
7435     isCellEditable : function(colIndex, rowIndex){
7436         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7437     },
7438
7439     /**
7440      * Returns the editor defined for the cell/column.
7441      * return false or null to disable editing.
7442      * @param {Number} colIndex The column index
7443      * @param {Number} rowIndex The row index
7444      * @return {Object}
7445      */
7446     getCellEditor : function(colIndex, rowIndex){
7447         return this.config[colIndex].editor;
7448     },
7449
7450     /**
7451      * Sets if a column is editable.
7452      * @param {Number} col The column index
7453      * @param {Boolean} editable True if the column is editable
7454      */
7455     setEditable : function(col, editable){
7456         this.config[col].editable = editable;
7457     },
7458
7459
7460     /**
7461      * Returns true if the column is hidden.
7462      * @param {Number} colIndex The column index
7463      * @return {Boolean}
7464      */
7465     isHidden : function(colIndex){
7466         return this.config[colIndex].hidden;
7467     },
7468
7469
7470     /**
7471      * Returns true if the column width cannot be changed
7472      */
7473     isFixed : function(colIndex){
7474         return this.config[colIndex].fixed;
7475     },
7476
7477     /**
7478      * Returns true if the column can be resized
7479      * @return {Boolean}
7480      */
7481     isResizable : function(colIndex){
7482         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7483     },
7484     /**
7485      * Sets if a column is hidden.
7486      * @param {Number} colIndex The column index
7487      * @param {Boolean} hidden True if the column is hidden
7488      */
7489     setHidden : function(colIndex, hidden){
7490         this.config[colIndex].hidden = hidden;
7491         this.totalWidth = null;
7492         this.fireEvent("hiddenchange", this, colIndex, hidden);
7493     },
7494
7495     /**
7496      * Sets the editor for a column.
7497      * @param {Number} col The column index
7498      * @param {Object} editor The editor object
7499      */
7500     setEditor : function(col, editor){
7501         this.config[col].editor = editor;
7502     }
7503 });
7504
7505 Roo.grid.ColumnModel.defaultRenderer = function(value)
7506 {
7507     if(typeof value == "object") {
7508         return value;
7509     }
7510         if(typeof value == "string" && value.length < 1){
7511             return "&#160;";
7512         }
7513     
7514         return String.format("{0}", value);
7515 };
7516
7517 // Alias for backwards compatibility
7518 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7519 /*
7520  * Based on:
7521  * Ext JS Library 1.1.1
7522  * Copyright(c) 2006-2007, Ext JS, LLC.
7523  *
7524  * Originally Released Under LGPL - original licence link has changed is not relivant.
7525  *
7526  * Fork - LGPL
7527  * <script type="text/javascript">
7528  */
7529  
7530 /**
7531  * @class Roo.LoadMask
7532  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7533  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7534  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7535  * element's UpdateManager load indicator and will be destroyed after the initial load.
7536  * @constructor
7537  * Create a new LoadMask
7538  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7539  * @param {Object} config The config object
7540  */
7541 Roo.LoadMask = function(el, config){
7542     this.el = Roo.get(el);
7543     Roo.apply(this, config);
7544     if(this.store){
7545         this.store.on('beforeload', this.onBeforeLoad, this);
7546         this.store.on('load', this.onLoad, this);
7547         this.store.on('loadexception', this.onLoadException, this);
7548         this.removeMask = false;
7549     }else{
7550         var um = this.el.getUpdateManager();
7551         um.showLoadIndicator = false; // disable the default indicator
7552         um.on('beforeupdate', this.onBeforeLoad, this);
7553         um.on('update', this.onLoad, this);
7554         um.on('failure', this.onLoad, this);
7555         this.removeMask = true;
7556     }
7557 };
7558
7559 Roo.LoadMask.prototype = {
7560     /**
7561      * @cfg {Boolean} removeMask
7562      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7563      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7564      */
7565     /**
7566      * @cfg {String} msg
7567      * The text to display in a centered loading message box (defaults to 'Loading...')
7568      */
7569     msg : 'Loading...',
7570     /**
7571      * @cfg {String} msgCls
7572      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7573      */
7574     msgCls : 'x-mask-loading',
7575
7576     /**
7577      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7578      * @type Boolean
7579      */
7580     disabled: false,
7581
7582     /**
7583      * Disables the mask to prevent it from being displayed
7584      */
7585     disable : function(){
7586        this.disabled = true;
7587     },
7588
7589     /**
7590      * Enables the mask so that it can be displayed
7591      */
7592     enable : function(){
7593         this.disabled = false;
7594     },
7595     
7596     onLoadException : function()
7597     {
7598         Roo.log(arguments);
7599         
7600         if (typeof(arguments[3]) != 'undefined') {
7601             Roo.MessageBox.alert("Error loading",arguments[3]);
7602         } 
7603         /*
7604         try {
7605             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7606                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7607             }   
7608         } catch(e) {
7609             
7610         }
7611         */
7612     
7613         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7614     },
7615     // private
7616     onLoad : function()
7617     {
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620
7621     // private
7622     onBeforeLoad : function(){
7623         if(!this.disabled){
7624             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7625         }
7626     },
7627
7628     // private
7629     destroy : function(){
7630         if(this.store){
7631             this.store.un('beforeload', this.onBeforeLoad, this);
7632             this.store.un('load', this.onLoad, this);
7633             this.store.un('loadexception', this.onLoadException, this);
7634         }else{
7635             var um = this.el.getUpdateManager();
7636             um.un('beforeupdate', this.onBeforeLoad, this);
7637             um.un('update', this.onLoad, this);
7638             um.un('failure', this.onLoad, this);
7639         }
7640     }
7641 };/*
7642  * - LGPL
7643  *
7644  * table
7645  * 
7646  */
7647
7648 /**
7649  * @class Roo.bootstrap.Table
7650  * @extends Roo.bootstrap.Component
7651  * Bootstrap Table class
7652  * @cfg {String} cls table class
7653  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7654  * @cfg {String} bgcolor Specifies the background color for a table
7655  * @cfg {Number} border Specifies whether the table cells should have borders or not
7656  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7657  * @cfg {Number} cellspacing Specifies the space between cells
7658  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7659  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7660  * @cfg {String} sortable Specifies that the table should be sortable
7661  * @cfg {String} summary Specifies a summary of the content of a table
7662  * @cfg {Number} width Specifies the width of a table
7663  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7664  * 
7665  * @cfg {boolean} striped Should the rows be alternative striped
7666  * @cfg {boolean} bordered Add borders to the table
7667  * @cfg {boolean} hover Add hover highlighting
7668  * @cfg {boolean} condensed Format condensed
7669  * @cfg {boolean} responsive Format condensed
7670  * @cfg {Boolean} loadMask (true|false) default false
7671  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7672  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7673  * @cfg {Boolean} rowSelection (true|false) default false
7674  * @cfg {Boolean} cellSelection (true|false) default false
7675  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7676  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7677  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7678  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7679  
7680  * 
7681  * @constructor
7682  * Create a new Table
7683  * @param {Object} config The config object
7684  */
7685
7686 Roo.bootstrap.Table = function(config){
7687     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7688     
7689   
7690     
7691     // BC...
7692     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7693     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7694     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7695     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7696     
7697     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7698     if (this.sm) {
7699         this.sm.grid = this;
7700         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7701         this.sm = this.selModel;
7702         this.sm.xmodule = this.xmodule || false;
7703     }
7704     
7705     if (this.cm && typeof(this.cm.config) == 'undefined') {
7706         this.colModel = new Roo.grid.ColumnModel(this.cm);
7707         this.cm = this.colModel;
7708         this.cm.xmodule = this.xmodule || false;
7709     }
7710     if (this.store) {
7711         this.store= Roo.factory(this.store, Roo.data);
7712         this.ds = this.store;
7713         this.ds.xmodule = this.xmodule || false;
7714          
7715     }
7716     if (this.footer && this.store) {
7717         this.footer.dataSource = this.ds;
7718         this.footer = Roo.factory(this.footer);
7719     }
7720     
7721     /** @private */
7722     this.addEvents({
7723         /**
7724          * @event cellclick
7725          * Fires when a cell is clicked
7726          * @param {Roo.bootstrap.Table} this
7727          * @param {Roo.Element} el
7728          * @param {Number} rowIndex
7729          * @param {Number} columnIndex
7730          * @param {Roo.EventObject} e
7731          */
7732         "cellclick" : true,
7733         /**
7734          * @event celldblclick
7735          * Fires when a cell is double clicked
7736          * @param {Roo.bootstrap.Table} this
7737          * @param {Roo.Element} el
7738          * @param {Number} rowIndex
7739          * @param {Number} columnIndex
7740          * @param {Roo.EventObject} e
7741          */
7742         "celldblclick" : true,
7743         /**
7744          * @event rowclick
7745          * Fires when a row is clicked
7746          * @param {Roo.bootstrap.Table} this
7747          * @param {Roo.Element} el
7748          * @param {Number} rowIndex
7749          * @param {Roo.EventObject} e
7750          */
7751         "rowclick" : true,
7752         /**
7753          * @event rowdblclick
7754          * Fires when a row is double clicked
7755          * @param {Roo.bootstrap.Table} this
7756          * @param {Roo.Element} el
7757          * @param {Number} rowIndex
7758          * @param {Roo.EventObject} e
7759          */
7760         "rowdblclick" : true,
7761         /**
7762          * @event mouseover
7763          * Fires when a mouseover occur
7764          * @param {Roo.bootstrap.Table} this
7765          * @param {Roo.Element} el
7766          * @param {Number} rowIndex
7767          * @param {Number} columnIndex
7768          * @param {Roo.EventObject} e
7769          */
7770         "mouseover" : true,
7771         /**
7772          * @event mouseout
7773          * Fires when a mouseout occur
7774          * @param {Roo.bootstrap.Table} this
7775          * @param {Roo.Element} el
7776          * @param {Number} rowIndex
7777          * @param {Number} columnIndex
7778          * @param {Roo.EventObject} e
7779          */
7780         "mouseout" : true,
7781         /**
7782          * @event rowclass
7783          * Fires when a row is rendered, so you can change add a style to it.
7784          * @param {Roo.bootstrap.Table} this
7785          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7786          */
7787         'rowclass' : true,
7788           /**
7789          * @event rowsrendered
7790          * Fires when all the  rows have been rendered
7791          * @param {Roo.bootstrap.Table} this
7792          */
7793         'rowsrendered' : true,
7794         /**
7795          * @event contextmenu
7796          * The raw contextmenu event for the entire grid.
7797          * @param {Roo.EventObject} e
7798          */
7799         "contextmenu" : true,
7800         /**
7801          * @event rowcontextmenu
7802          * Fires when a row is right clicked
7803          * @param {Roo.bootstrap.Table} this
7804          * @param {Number} rowIndex
7805          * @param {Roo.EventObject} e
7806          */
7807         "rowcontextmenu" : true,
7808         /**
7809          * @event cellcontextmenu
7810          * Fires when a cell is right clicked
7811          * @param {Roo.bootstrap.Table} this
7812          * @param {Number} rowIndex
7813          * @param {Number} cellIndex
7814          * @param {Roo.EventObject} e
7815          */
7816          "cellcontextmenu" : true,
7817          /**
7818          * @event headercontextmenu
7819          * Fires when a header is right clicked
7820          * @param {Roo.bootstrap.Table} this
7821          * @param {Number} columnIndex
7822          * @param {Roo.EventObject} e
7823          */
7824         "headercontextmenu" : true
7825     });
7826 };
7827
7828 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7829     
7830     cls: false,
7831     align: false,
7832     bgcolor: false,
7833     border: false,
7834     cellpadding: false,
7835     cellspacing: false,
7836     frame: false,
7837     rules: false,
7838     sortable: false,
7839     summary: false,
7840     width: false,
7841     striped : false,
7842     scrollBody : false,
7843     bordered: false,
7844     hover:  false,
7845     condensed : false,
7846     responsive : false,
7847     sm : false,
7848     cm : false,
7849     store : false,
7850     loadMask : false,
7851     footerShow : true,
7852     headerShow : true,
7853   
7854     rowSelection : false,
7855     cellSelection : false,
7856     layout : false,
7857     
7858     // Roo.Element - the tbody
7859     mainBody: false,
7860     // Roo.Element - thead element
7861     mainHead: false,
7862     
7863     container: false, // used by gridpanel...
7864     
7865     lazyLoad : false,
7866     
7867     CSS : Roo.util.CSS,
7868     
7869     auto_hide_footer : false,
7870     
7871     getAutoCreate : function()
7872     {
7873         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7874         
7875         cfg = {
7876             tag: 'table',
7877             cls : 'table',
7878             cn : []
7879         };
7880         if (this.scrollBody) {
7881             cfg.cls += ' table-body-fixed';
7882         }    
7883         if (this.striped) {
7884             cfg.cls += ' table-striped';
7885         }
7886         
7887         if (this.hover) {
7888             cfg.cls += ' table-hover';
7889         }
7890         if (this.bordered) {
7891             cfg.cls += ' table-bordered';
7892         }
7893         if (this.condensed) {
7894             cfg.cls += ' table-condensed';
7895         }
7896         if (this.responsive) {
7897             cfg.cls += ' table-responsive';
7898         }
7899         
7900         if (this.cls) {
7901             cfg.cls+=  ' ' +this.cls;
7902         }
7903         
7904         // this lot should be simplifed...
7905         var _t = this;
7906         var cp = [
7907             'align',
7908             'bgcolor',
7909             'border',
7910             'cellpadding',
7911             'cellspacing',
7912             'frame',
7913             'rules',
7914             'sortable',
7915             'summary',
7916             'width'
7917         ].forEach(function(k) {
7918             if (_t[k]) {
7919                 cfg[k] = _t[k];
7920             }
7921         });
7922         
7923         
7924         if (this.layout) {
7925             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7926         }
7927         
7928         if(this.store || this.cm){
7929             if(this.headerShow){
7930                 cfg.cn.push(this.renderHeader());
7931             }
7932             
7933             cfg.cn.push(this.renderBody());
7934             
7935             if(this.footerShow){
7936                 cfg.cn.push(this.renderFooter());
7937             }
7938             // where does this come from?
7939             //cfg.cls+=  ' TableGrid';
7940         }
7941         
7942         return { cn : [ cfg ] };
7943     },
7944     
7945     initEvents : function()
7946     {   
7947         if(!this.store || !this.cm){
7948             return;
7949         }
7950         if (this.selModel) {
7951             this.selModel.initEvents();
7952         }
7953         
7954         
7955         //Roo.log('initEvents with ds!!!!');
7956         
7957         this.mainBody = this.el.select('tbody', true).first();
7958         this.mainHead = this.el.select('thead', true).first();
7959         this.mainFoot = this.el.select('tfoot', true).first();
7960         
7961         
7962         
7963         var _this = this;
7964         
7965         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7966             e.on('click', _this.sort, _this);
7967         });
7968         
7969         this.mainBody.on("click", this.onClick, this);
7970         this.mainBody.on("dblclick", this.onDblClick, this);
7971         
7972         // why is this done????? = it breaks dialogs??
7973         //this.parent().el.setStyle('position', 'relative');
7974         
7975         
7976         if (this.footer) {
7977             this.footer.parentId = this.id;
7978             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7979             
7980             if(this.lazyLoad){
7981                 this.el.select('tfoot tr td').first().addClass('hide');
7982             }
7983         } 
7984         
7985         if(this.loadMask) {
7986             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7987         }
7988         
7989         this.store.on('load', this.onLoad, this);
7990         this.store.on('beforeload', this.onBeforeLoad, this);
7991         this.store.on('update', this.onUpdate, this);
7992         this.store.on('add', this.onAdd, this);
7993         this.store.on("clear", this.clear, this);
7994         
7995         this.el.on("contextmenu", this.onContextMenu, this);
7996         
7997         this.mainBody.on('scroll', this.onBodyScroll, this);
7998         
7999         this.cm.on("headerchange", this.onHeaderChange, this);
8000         
8001         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8002         
8003     },
8004     
8005     onContextMenu : function(e, t)
8006     {
8007         this.processEvent("contextmenu", e);
8008     },
8009     
8010     processEvent : function(name, e)
8011     {
8012         if (name != 'touchstart' ) {
8013             this.fireEvent(name, e);    
8014         }
8015         
8016         var t = e.getTarget();
8017         
8018         var cell = Roo.get(t);
8019         
8020         if(!cell){
8021             return;
8022         }
8023         
8024         if(cell.findParent('tfoot', false, true)){
8025             return;
8026         }
8027         
8028         if(cell.findParent('thead', false, true)){
8029             
8030             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8031                 cell = Roo.get(t).findParent('th', false, true);
8032                 if (!cell) {
8033                     Roo.log("failed to find th in thead?");
8034                     Roo.log(e.getTarget());
8035                     return;
8036                 }
8037             }
8038             
8039             var cellIndex = cell.dom.cellIndex;
8040             
8041             var ename = name == 'touchstart' ? 'click' : name;
8042             this.fireEvent("header" + ename, this, cellIndex, e);
8043             
8044             return;
8045         }
8046         
8047         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8048             cell = Roo.get(t).findParent('td', false, true);
8049             if (!cell) {
8050                 Roo.log("failed to find th in tbody?");
8051                 Roo.log(e.getTarget());
8052                 return;
8053             }
8054         }
8055         
8056         var row = cell.findParent('tr', false, true);
8057         var cellIndex = cell.dom.cellIndex;
8058         var rowIndex = row.dom.rowIndex - 1;
8059         
8060         if(row !== false){
8061             
8062             this.fireEvent("row" + name, this, rowIndex, e);
8063             
8064             if(cell !== false){
8065             
8066                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8067             }
8068         }
8069         
8070     },
8071     
8072     onMouseover : function(e, el)
8073     {
8074         var cell = Roo.get(el);
8075         
8076         if(!cell){
8077             return;
8078         }
8079         
8080         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8081             cell = cell.findParent('td', false, true);
8082         }
8083         
8084         var row = cell.findParent('tr', false, true);
8085         var cellIndex = cell.dom.cellIndex;
8086         var rowIndex = row.dom.rowIndex - 1; // start from 0
8087         
8088         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8089         
8090     },
8091     
8092     onMouseout : function(e, el)
8093     {
8094         var cell = Roo.get(el);
8095         
8096         if(!cell){
8097             return;
8098         }
8099         
8100         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8101             cell = cell.findParent('td', false, true);
8102         }
8103         
8104         var row = cell.findParent('tr', false, true);
8105         var cellIndex = cell.dom.cellIndex;
8106         var rowIndex = row.dom.rowIndex - 1; // start from 0
8107         
8108         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8109         
8110     },
8111     
8112     onClick : function(e, el)
8113     {
8114         var cell = Roo.get(el);
8115         
8116         if(!cell || (!this.cellSelection && !this.rowSelection)){
8117             return;
8118         }
8119         
8120         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8121             cell = cell.findParent('td', false, true);
8122         }
8123         
8124         if(!cell || typeof(cell) == 'undefined'){
8125             return;
8126         }
8127         
8128         var row = cell.findParent('tr', false, true);
8129         
8130         if(!row || typeof(row) == 'undefined'){
8131             return;
8132         }
8133         
8134         var cellIndex = cell.dom.cellIndex;
8135         var rowIndex = this.getRowIndex(row);
8136         
8137         // why??? - should these not be based on SelectionModel?
8138         if(this.cellSelection){
8139             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8140         }
8141         
8142         if(this.rowSelection){
8143             this.fireEvent('rowclick', this, row, rowIndex, e);
8144         }
8145         
8146         
8147     },
8148         
8149     onDblClick : function(e,el)
8150     {
8151         var cell = Roo.get(el);
8152         
8153         if(!cell || (!this.cellSelection && !this.rowSelection)){
8154             return;
8155         }
8156         
8157         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8158             cell = cell.findParent('td', false, true);
8159         }
8160         
8161         if(!cell || typeof(cell) == 'undefined'){
8162             return;
8163         }
8164         
8165         var row = cell.findParent('tr', false, true);
8166         
8167         if(!row || typeof(row) == 'undefined'){
8168             return;
8169         }
8170         
8171         var cellIndex = cell.dom.cellIndex;
8172         var rowIndex = this.getRowIndex(row);
8173         
8174         if(this.cellSelection){
8175             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8176         }
8177         
8178         if(this.rowSelection){
8179             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8180         }
8181     },
8182     
8183     sort : function(e,el)
8184     {
8185         var col = Roo.get(el);
8186         
8187         if(!col.hasClass('sortable')){
8188             return;
8189         }
8190         
8191         var sort = col.attr('sort');
8192         var dir = 'ASC';
8193         
8194         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8195             dir = 'DESC';
8196         }
8197         
8198         this.store.sortInfo = {field : sort, direction : dir};
8199         
8200         if (this.footer) {
8201             Roo.log("calling footer first");
8202             this.footer.onClick('first');
8203         } else {
8204         
8205             this.store.load({ params : { start : 0 } });
8206         }
8207     },
8208     
8209     renderHeader : function()
8210     {
8211         var header = {
8212             tag: 'thead',
8213             cn : []
8214         };
8215         
8216         var cm = this.cm;
8217         this.totalWidth = 0;
8218         
8219         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8220             
8221             var config = cm.config[i];
8222             
8223             var c = {
8224                 tag: 'th',
8225                 cls : 'x-hcol-' + i,
8226                 style : '',
8227                 html: cm.getColumnHeader(i)
8228             };
8229             
8230             var hh = '';
8231             
8232             if(typeof(config.sortable) != 'undefined' && config.sortable){
8233                 c.cls = 'sortable';
8234                 c.html = '<i class="glyphicon"></i>' + c.html;
8235             }
8236             
8237             // could use BS4 hidden-..-down 
8238             
8239             if(typeof(config.lgHeader) != 'undefined'){
8240                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8241             }
8242             
8243             if(typeof(config.mdHeader) != 'undefined'){
8244                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8245             }
8246             
8247             if(typeof(config.smHeader) != 'undefined'){
8248                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8249             }
8250             
8251             if(typeof(config.xsHeader) != 'undefined'){
8252                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8253             }
8254             
8255             if(hh.length){
8256                 c.html = hh;
8257             }
8258             
8259             if(typeof(config.tooltip) != 'undefined'){
8260                 c.tooltip = config.tooltip;
8261             }
8262             
8263             if(typeof(config.colspan) != 'undefined'){
8264                 c.colspan = config.colspan;
8265             }
8266             
8267             if(typeof(config.hidden) != 'undefined' && config.hidden){
8268                 c.style += ' display:none;';
8269             }
8270             
8271             if(typeof(config.dataIndex) != 'undefined'){
8272                 c.sort = config.dataIndex;
8273             }
8274             
8275            
8276             
8277             if(typeof(config.align) != 'undefined' && config.align.length){
8278                 c.style += ' text-align:' + config.align + ';';
8279             }
8280             
8281             if(typeof(config.width) != 'undefined'){
8282                 c.style += ' width:' + config.width + 'px;';
8283                 this.totalWidth += config.width;
8284             } else {
8285                 this.totalWidth += 100; // assume minimum of 100 per column?
8286             }
8287             
8288             if(typeof(config.cls) != 'undefined'){
8289                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8290             }
8291             
8292             ['xs','sm','md','lg'].map(function(size){
8293                 
8294                 if(typeof(config[size]) == 'undefined'){
8295                     return;
8296                 }
8297                  
8298                 if (!config[size]) { // 0 = hidden
8299                     // BS 4 '0' is treated as hide that column and below.
8300                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8301                     return;
8302                 }
8303                 
8304                 c.cls += ' col-' + size + '-' + config[size] + (
8305                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8306                 );
8307                 
8308                 
8309             });
8310             
8311             header.cn.push(c)
8312         }
8313         
8314         return header;
8315     },
8316     
8317     renderBody : function()
8318     {
8319         var body = {
8320             tag: 'tbody',
8321             cn : [
8322                 {
8323                     tag: 'tr',
8324                     cn : [
8325                         {
8326                             tag : 'td',
8327                             colspan :  this.cm.getColumnCount()
8328                         }
8329                     ]
8330                 }
8331             ]
8332         };
8333         
8334         return body;
8335     },
8336     
8337     renderFooter : function()
8338     {
8339         var footer = {
8340             tag: 'tfoot',
8341             cn : [
8342                 {
8343                     tag: 'tr',
8344                     cn : [
8345                         {
8346                             tag : 'td',
8347                             colspan :  this.cm.getColumnCount()
8348                         }
8349                     ]
8350                 }
8351             ]
8352         };
8353         
8354         return footer;
8355     },
8356     
8357     
8358     
8359     onLoad : function()
8360     {
8361 //        Roo.log('ds onload');
8362         this.clear();
8363         
8364         var _this = this;
8365         var cm = this.cm;
8366         var ds = this.store;
8367         
8368         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8369             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8370             if (_this.store.sortInfo) {
8371                     
8372                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8373                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8374                 }
8375                 
8376                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8377                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8378                 }
8379             }
8380         });
8381         
8382         var tbody =  this.mainBody;
8383               
8384         if(ds.getCount() > 0){
8385             ds.data.each(function(d,rowIndex){
8386                 var row =  this.renderRow(cm, ds, rowIndex);
8387                 
8388                 tbody.createChild(row);
8389                 
8390                 var _this = this;
8391                 
8392                 if(row.cellObjects.length){
8393                     Roo.each(row.cellObjects, function(r){
8394                         _this.renderCellObject(r);
8395                     })
8396                 }
8397                 
8398             }, this);
8399         }
8400         
8401         var tfoot = this.el.select('tfoot', true).first();
8402         
8403         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8404             
8405             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8406             
8407             var total = this.ds.getTotalCount();
8408             
8409             if(this.footer.pageSize < total){
8410                 this.mainFoot.show();
8411             }
8412         }
8413         
8414         Roo.each(this.el.select('tbody td', true).elements, function(e){
8415             e.on('mouseover', _this.onMouseover, _this);
8416         });
8417         
8418         Roo.each(this.el.select('tbody td', true).elements, function(e){
8419             e.on('mouseout', _this.onMouseout, _this);
8420         });
8421         this.fireEvent('rowsrendered', this);
8422         
8423         this.autoSize();
8424     },
8425     
8426     
8427     onUpdate : function(ds,record)
8428     {
8429         this.refreshRow(record);
8430         this.autoSize();
8431     },
8432     
8433     onRemove : function(ds, record, index, isUpdate){
8434         if(isUpdate !== true){
8435             this.fireEvent("beforerowremoved", this, index, record);
8436         }
8437         var bt = this.mainBody.dom;
8438         
8439         var rows = this.el.select('tbody > tr', true).elements;
8440         
8441         if(typeof(rows[index]) != 'undefined'){
8442             bt.removeChild(rows[index].dom);
8443         }
8444         
8445 //        if(bt.rows[index]){
8446 //            bt.removeChild(bt.rows[index]);
8447 //        }
8448         
8449         if(isUpdate !== true){
8450             //this.stripeRows(index);
8451             //this.syncRowHeights(index, index);
8452             //this.layout();
8453             this.fireEvent("rowremoved", this, index, record);
8454         }
8455     },
8456     
8457     onAdd : function(ds, records, rowIndex)
8458     {
8459         //Roo.log('on Add called');
8460         // - note this does not handle multiple adding very well..
8461         var bt = this.mainBody.dom;
8462         for (var i =0 ; i < records.length;i++) {
8463             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8464             //Roo.log(records[i]);
8465             //Roo.log(this.store.getAt(rowIndex+i));
8466             this.insertRow(this.store, rowIndex + i, false);
8467             return;
8468         }
8469         
8470     },
8471     
8472     
8473     refreshRow : function(record){
8474         var ds = this.store, index;
8475         if(typeof record == 'number'){
8476             index = record;
8477             record = ds.getAt(index);
8478         }else{
8479             index = ds.indexOf(record);
8480             if (index < 0) {
8481                 return; // should not happen - but seems to 
8482             }
8483         }
8484         this.insertRow(ds, index, true);
8485         this.autoSize();
8486         this.onRemove(ds, record, index+1, true);
8487         this.autoSize();
8488         //this.syncRowHeights(index, index);
8489         //this.layout();
8490         this.fireEvent("rowupdated", this, index, record);
8491     },
8492     
8493     insertRow : function(dm, rowIndex, isUpdate){
8494         
8495         if(!isUpdate){
8496             this.fireEvent("beforerowsinserted", this, rowIndex);
8497         }
8498             //var s = this.getScrollState();
8499         var row = this.renderRow(this.cm, this.store, rowIndex);
8500         // insert before rowIndex..
8501         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8502         
8503         var _this = this;
8504                 
8505         if(row.cellObjects.length){
8506             Roo.each(row.cellObjects, function(r){
8507                 _this.renderCellObject(r);
8508             })
8509         }
8510             
8511         if(!isUpdate){
8512             this.fireEvent("rowsinserted", this, rowIndex);
8513             //this.syncRowHeights(firstRow, lastRow);
8514             //this.stripeRows(firstRow);
8515             //this.layout();
8516         }
8517         
8518     },
8519     
8520     
8521     getRowDom : function(rowIndex)
8522     {
8523         var rows = this.el.select('tbody > tr', true).elements;
8524         
8525         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8526         
8527     },
8528     // returns the object tree for a tr..
8529   
8530     
8531     renderRow : function(cm, ds, rowIndex) 
8532     {
8533         var d = ds.getAt(rowIndex);
8534         
8535         var row = {
8536             tag : 'tr',
8537             cls : 'x-row-' + rowIndex,
8538             cn : []
8539         };
8540             
8541         var cellObjects = [];
8542         
8543         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8544             var config = cm.config[i];
8545             
8546             var renderer = cm.getRenderer(i);
8547             var value = '';
8548             var id = false;
8549             
8550             if(typeof(renderer) !== 'undefined'){
8551                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8552             }
8553             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8554             // and are rendered into the cells after the row is rendered - using the id for the element.
8555             
8556             if(typeof(value) === 'object'){
8557                 id = Roo.id();
8558                 cellObjects.push({
8559                     container : id,
8560                     cfg : value 
8561                 })
8562             }
8563             
8564             var rowcfg = {
8565                 record: d,
8566                 rowIndex : rowIndex,
8567                 colIndex : i,
8568                 rowClass : ''
8569             };
8570
8571             this.fireEvent('rowclass', this, rowcfg);
8572             
8573             var td = {
8574                 tag: 'td',
8575                 cls : rowcfg.rowClass + ' x-col-' + i,
8576                 style: '',
8577                 html: (typeof(value) === 'object') ? '' : value
8578             };
8579             
8580             if (id) {
8581                 td.id = id;
8582             }
8583             
8584             if(typeof(config.colspan) != 'undefined'){
8585                 td.colspan = config.colspan;
8586             }
8587             
8588             if(typeof(config.hidden) != 'undefined' && config.hidden){
8589                 td.style += ' display:none;';
8590             }
8591             
8592             if(typeof(config.align) != 'undefined' && config.align.length){
8593                 td.style += ' text-align:' + config.align + ';';
8594             }
8595             if(typeof(config.valign) != 'undefined' && config.valign.length){
8596                 td.style += ' vertical-align:' + config.valign + ';';
8597             }
8598             
8599             if(typeof(config.width) != 'undefined'){
8600                 td.style += ' width:' +  config.width + 'px;';
8601             }
8602             
8603             if(typeof(config.cursor) != 'undefined'){
8604                 td.style += ' cursor:' +  config.cursor + ';';
8605             }
8606             
8607             if(typeof(config.cls) != 'undefined'){
8608                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8609             }
8610             
8611             ['xs','sm','md','lg'].map(function(size){
8612                 
8613                 if(typeof(config[size]) == 'undefined'){
8614                     return;
8615                 }
8616                 
8617                 
8618                   
8619                 if (!config[size]) { // 0 = hidden
8620                     // BS 4 '0' is treated as hide that column and below.
8621                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8622                     return;
8623                 }
8624                 
8625                 td.cls += ' col-' + size + '-' + config[size] + (
8626                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8627                 );
8628                  
8629
8630             });
8631             
8632             row.cn.push(td);
8633            
8634         }
8635         
8636         row.cellObjects = cellObjects;
8637         
8638         return row;
8639           
8640     },
8641     
8642     
8643     
8644     onBeforeLoad : function()
8645     {
8646         
8647     },
8648      /**
8649      * Remove all rows
8650      */
8651     clear : function()
8652     {
8653         this.el.select('tbody', true).first().dom.innerHTML = '';
8654     },
8655     /**
8656      * Show or hide a row.
8657      * @param {Number} rowIndex to show or hide
8658      * @param {Boolean} state hide
8659      */
8660     setRowVisibility : function(rowIndex, state)
8661     {
8662         var bt = this.mainBody.dom;
8663         
8664         var rows = this.el.select('tbody > tr', true).elements;
8665         
8666         if(typeof(rows[rowIndex]) == 'undefined'){
8667             return;
8668         }
8669         rows[rowIndex].dom.style.display = state ? '' : 'none';
8670     },
8671     
8672     
8673     getSelectionModel : function(){
8674         if(!this.selModel){
8675             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8676         }
8677         return this.selModel;
8678     },
8679     /*
8680      * Render the Roo.bootstrap object from renderder
8681      */
8682     renderCellObject : function(r)
8683     {
8684         var _this = this;
8685         
8686         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8687         
8688         var t = r.cfg.render(r.container);
8689         
8690         if(r.cfg.cn){
8691             Roo.each(r.cfg.cn, function(c){
8692                 var child = {
8693                     container: t.getChildContainer(),
8694                     cfg: c
8695                 };
8696                 _this.renderCellObject(child);
8697             })
8698         }
8699     },
8700     
8701     getRowIndex : function(row)
8702     {
8703         var rowIndex = -1;
8704         
8705         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8706             if(el != row){
8707                 return;
8708             }
8709             
8710             rowIndex = index;
8711         });
8712         
8713         return rowIndex;
8714     },
8715      /**
8716      * Returns the grid's underlying element = used by panel.Grid
8717      * @return {Element} The element
8718      */
8719     getGridEl : function(){
8720         return this.el;
8721     },
8722      /**
8723      * Forces a resize - used by panel.Grid
8724      * @return {Element} The element
8725      */
8726     autoSize : function()
8727     {
8728         //var ctr = Roo.get(this.container.dom.parentElement);
8729         var ctr = Roo.get(this.el.dom);
8730         
8731         var thd = this.getGridEl().select('thead',true).first();
8732         var tbd = this.getGridEl().select('tbody', true).first();
8733         var tfd = this.getGridEl().select('tfoot', true).first();
8734         
8735         var cw = ctr.getWidth();
8736         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8737         
8738         if (tbd) {
8739             
8740             tbd.setWidth(ctr.getWidth());
8741             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8742             // this needs fixing for various usage - currently only hydra job advers I think..
8743             //tdb.setHeight(
8744             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8745             //); 
8746             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8747             cw -= barsize;
8748         }
8749         cw = Math.max(cw, this.totalWidth);
8750         this.getGridEl().select('tbody tr',true).setWidth(cw);
8751         
8752         // resize 'expandable coloumn?
8753         
8754         return; // we doe not have a view in this design..
8755         
8756     },
8757     onBodyScroll: function()
8758     {
8759         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8760         if(this.mainHead){
8761             this.mainHead.setStyle({
8762                 'position' : 'relative',
8763                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8764             });
8765         }
8766         
8767         if(this.lazyLoad){
8768             
8769             var scrollHeight = this.mainBody.dom.scrollHeight;
8770             
8771             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8772             
8773             var height = this.mainBody.getHeight();
8774             
8775             if(scrollHeight - height == scrollTop) {
8776                 
8777                 var total = this.ds.getTotalCount();
8778                 
8779                 if(this.footer.cursor + this.footer.pageSize < total){
8780                     
8781                     this.footer.ds.load({
8782                         params : {
8783                             start : this.footer.cursor + this.footer.pageSize,
8784                             limit : this.footer.pageSize
8785                         },
8786                         add : true
8787                     });
8788                 }
8789             }
8790             
8791         }
8792     },
8793     
8794     onHeaderChange : function()
8795     {
8796         var header = this.renderHeader();
8797         var table = this.el.select('table', true).first();
8798         
8799         this.mainHead.remove();
8800         this.mainHead = table.createChild(header, this.mainBody, false);
8801     },
8802     
8803     onHiddenChange : function(colModel, colIndex, hidden)
8804     {
8805         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8806         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8807         
8808         this.CSS.updateRule(thSelector, "display", "");
8809         this.CSS.updateRule(tdSelector, "display", "");
8810         
8811         if(hidden){
8812             this.CSS.updateRule(thSelector, "display", "none");
8813             this.CSS.updateRule(tdSelector, "display", "none");
8814         }
8815         
8816         this.onHeaderChange();
8817         this.onLoad();
8818     },
8819     
8820     setColumnWidth: function(col_index, width)
8821     {
8822         // width = "md-2 xs-2..."
8823         if(!this.colModel.config[col_index]) {
8824             return;
8825         }
8826         
8827         var w = width.split(" ");
8828         
8829         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8830         
8831         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8832         
8833         
8834         for(var j = 0; j < w.length; j++) {
8835             
8836             if(!w[j]) {
8837                 continue;
8838             }
8839             
8840             var size_cls = w[j].split("-");
8841             
8842             if(!Number.isInteger(size_cls[1] * 1)) {
8843                 continue;
8844             }
8845             
8846             if(!this.colModel.config[col_index][size_cls[0]]) {
8847                 continue;
8848             }
8849             
8850             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8851                 continue;
8852             }
8853             
8854             h_row[0].classList.replace(
8855                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8856                 "col-"+size_cls[0]+"-"+size_cls[1]
8857             );
8858             
8859             for(var i = 0; i < rows.length; i++) {
8860                 
8861                 var size_cls = w[j].split("-");
8862                 
8863                 if(!Number.isInteger(size_cls[1] * 1)) {
8864                     continue;
8865                 }
8866                 
8867                 if(!this.colModel.config[col_index][size_cls[0]]) {
8868                     continue;
8869                 }
8870                 
8871                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8872                     continue;
8873                 }
8874                 
8875                 rows[i].classList.replace(
8876                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8877                     "col-"+size_cls[0]+"-"+size_cls[1]
8878                 );
8879             }
8880             
8881             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8882         }
8883     }
8884 });
8885
8886  
8887
8888  /*
8889  * - LGPL
8890  *
8891  * table cell
8892  * 
8893  */
8894
8895 /**
8896  * @class Roo.bootstrap.TableCell
8897  * @extends Roo.bootstrap.Component
8898  * Bootstrap TableCell class
8899  * @cfg {String} html cell contain text
8900  * @cfg {String} cls cell class
8901  * @cfg {String} tag cell tag (td|th) default td
8902  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8903  * @cfg {String} align Aligns the content in a cell
8904  * @cfg {String} axis Categorizes cells
8905  * @cfg {String} bgcolor Specifies the background color of a cell
8906  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8907  * @cfg {Number} colspan Specifies the number of columns a cell should span
8908  * @cfg {String} headers Specifies one or more header cells a cell is related to
8909  * @cfg {Number} height Sets the height of a cell
8910  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8911  * @cfg {Number} rowspan Sets the number of rows a cell should span
8912  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8913  * @cfg {String} valign Vertical aligns the content in a cell
8914  * @cfg {Number} width Specifies the width of a cell
8915  * 
8916  * @constructor
8917  * Create a new TableCell
8918  * @param {Object} config The config object
8919  */
8920
8921 Roo.bootstrap.TableCell = function(config){
8922     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8923 };
8924
8925 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8926     
8927     html: false,
8928     cls: false,
8929     tag: false,
8930     abbr: false,
8931     align: false,
8932     axis: false,
8933     bgcolor: false,
8934     charoff: false,
8935     colspan: false,
8936     headers: false,
8937     height: false,
8938     nowrap: false,
8939     rowspan: false,
8940     scope: false,
8941     valign: false,
8942     width: false,
8943     
8944     
8945     getAutoCreate : function(){
8946         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8947         
8948         cfg = {
8949             tag: 'td'
8950         };
8951         
8952         if(this.tag){
8953             cfg.tag = this.tag;
8954         }
8955         
8956         if (this.html) {
8957             cfg.html=this.html
8958         }
8959         if (this.cls) {
8960             cfg.cls=this.cls
8961         }
8962         if (this.abbr) {
8963             cfg.abbr=this.abbr
8964         }
8965         if (this.align) {
8966             cfg.align=this.align
8967         }
8968         if (this.axis) {
8969             cfg.axis=this.axis
8970         }
8971         if (this.bgcolor) {
8972             cfg.bgcolor=this.bgcolor
8973         }
8974         if (this.charoff) {
8975             cfg.charoff=this.charoff
8976         }
8977         if (this.colspan) {
8978             cfg.colspan=this.colspan
8979         }
8980         if (this.headers) {
8981             cfg.headers=this.headers
8982         }
8983         if (this.height) {
8984             cfg.height=this.height
8985         }
8986         if (this.nowrap) {
8987             cfg.nowrap=this.nowrap
8988         }
8989         if (this.rowspan) {
8990             cfg.rowspan=this.rowspan
8991         }
8992         if (this.scope) {
8993             cfg.scope=this.scope
8994         }
8995         if (this.valign) {
8996             cfg.valign=this.valign
8997         }
8998         if (this.width) {
8999             cfg.width=this.width
9000         }
9001         
9002         
9003         return cfg;
9004     }
9005    
9006 });
9007
9008  
9009
9010  /*
9011  * - LGPL
9012  *
9013  * table row
9014  * 
9015  */
9016
9017 /**
9018  * @class Roo.bootstrap.TableRow
9019  * @extends Roo.bootstrap.Component
9020  * Bootstrap TableRow class
9021  * @cfg {String} cls row class
9022  * @cfg {String} align Aligns the content in a table row
9023  * @cfg {String} bgcolor Specifies a background color for a table row
9024  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9025  * @cfg {String} valign Vertical aligns the content in a table row
9026  * 
9027  * @constructor
9028  * Create a new TableRow
9029  * @param {Object} config The config object
9030  */
9031
9032 Roo.bootstrap.TableRow = function(config){
9033     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9034 };
9035
9036 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9037     
9038     cls: false,
9039     align: false,
9040     bgcolor: false,
9041     charoff: false,
9042     valign: false,
9043     
9044     getAutoCreate : function(){
9045         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9046         
9047         cfg = {
9048             tag: 'tr'
9049         };
9050             
9051         if(this.cls){
9052             cfg.cls = this.cls;
9053         }
9054         if(this.align){
9055             cfg.align = this.align;
9056         }
9057         if(this.bgcolor){
9058             cfg.bgcolor = this.bgcolor;
9059         }
9060         if(this.charoff){
9061             cfg.charoff = this.charoff;
9062         }
9063         if(this.valign){
9064             cfg.valign = this.valign;
9065         }
9066         
9067         return cfg;
9068     }
9069    
9070 });
9071
9072  
9073
9074  /*
9075  * - LGPL
9076  *
9077  * table body
9078  * 
9079  */
9080
9081 /**
9082  * @class Roo.bootstrap.TableBody
9083  * @extends Roo.bootstrap.Component
9084  * Bootstrap TableBody class
9085  * @cfg {String} cls element class
9086  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9087  * @cfg {String} align Aligns the content inside the element
9088  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9089  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9090  * 
9091  * @constructor
9092  * Create a new TableBody
9093  * @param {Object} config The config object
9094  */
9095
9096 Roo.bootstrap.TableBody = function(config){
9097     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9098 };
9099
9100 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9101     
9102     cls: false,
9103     tag: false,
9104     align: false,
9105     charoff: false,
9106     valign: false,
9107     
9108     getAutoCreate : function(){
9109         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9110         
9111         cfg = {
9112             tag: 'tbody'
9113         };
9114             
9115         if (this.cls) {
9116             cfg.cls=this.cls
9117         }
9118         if(this.tag){
9119             cfg.tag = this.tag;
9120         }
9121         
9122         if(this.align){
9123             cfg.align = this.align;
9124         }
9125         if(this.charoff){
9126             cfg.charoff = this.charoff;
9127         }
9128         if(this.valign){
9129             cfg.valign = this.valign;
9130         }
9131         
9132         return cfg;
9133     }
9134     
9135     
9136 //    initEvents : function()
9137 //    {
9138 //        
9139 //        if(!this.store){
9140 //            return;
9141 //        }
9142 //        
9143 //        this.store = Roo.factory(this.store, Roo.data);
9144 //        this.store.on('load', this.onLoad, this);
9145 //        
9146 //        this.store.load();
9147 //        
9148 //    },
9149 //    
9150 //    onLoad: function () 
9151 //    {   
9152 //        this.fireEvent('load', this);
9153 //    }
9154 //    
9155 //   
9156 });
9157
9158  
9159
9160  /*
9161  * Based on:
9162  * Ext JS Library 1.1.1
9163  * Copyright(c) 2006-2007, Ext JS, LLC.
9164  *
9165  * Originally Released Under LGPL - original licence link has changed is not relivant.
9166  *
9167  * Fork - LGPL
9168  * <script type="text/javascript">
9169  */
9170
9171 // as we use this in bootstrap.
9172 Roo.namespace('Roo.form');
9173  /**
9174  * @class Roo.form.Action
9175  * Internal Class used to handle form actions
9176  * @constructor
9177  * @param {Roo.form.BasicForm} el The form element or its id
9178  * @param {Object} config Configuration options
9179  */
9180
9181  
9182  
9183 // define the action interface
9184 Roo.form.Action = function(form, options){
9185     this.form = form;
9186     this.options = options || {};
9187 };
9188 /**
9189  * Client Validation Failed
9190  * @const 
9191  */
9192 Roo.form.Action.CLIENT_INVALID = 'client';
9193 /**
9194  * Server Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.SERVER_INVALID = 'server';
9198  /**
9199  * Connect to Server Failed
9200  * @const 
9201  */
9202 Roo.form.Action.CONNECT_FAILURE = 'connect';
9203 /**
9204  * Reading Data from Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.LOAD_FAILURE = 'load';
9208
9209 Roo.form.Action.prototype = {
9210     type : 'default',
9211     failureType : undefined,
9212     response : undefined,
9213     result : undefined,
9214
9215     // interface method
9216     run : function(options){
9217
9218     },
9219
9220     // interface method
9221     success : function(response){
9222
9223     },
9224
9225     // interface method
9226     handleResponse : function(response){
9227
9228     },
9229
9230     // default connection failure
9231     failure : function(response){
9232         
9233         this.response = response;
9234         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9235         this.form.afterAction(this, false);
9236     },
9237
9238     processResponse : function(response){
9239         this.response = response;
9240         if(!response.responseText){
9241             return true;
9242         }
9243         this.result = this.handleResponse(response);
9244         return this.result;
9245     },
9246
9247     // utility functions used internally
9248     getUrl : function(appendParams){
9249         var url = this.options.url || this.form.url || this.form.el.dom.action;
9250         if(appendParams){
9251             var p = this.getParams();
9252             if(p){
9253                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9254             }
9255         }
9256         return url;
9257     },
9258
9259     getMethod : function(){
9260         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9261     },
9262
9263     getParams : function(){
9264         var bp = this.form.baseParams;
9265         var p = this.options.params;
9266         if(p){
9267             if(typeof p == "object"){
9268                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9269             }else if(typeof p == 'string' && bp){
9270                 p += '&' + Roo.urlEncode(bp);
9271             }
9272         }else if(bp){
9273             p = Roo.urlEncode(bp);
9274         }
9275         return p;
9276     },
9277
9278     createCallback : function(){
9279         return {
9280             success: this.success,
9281             failure: this.failure,
9282             scope: this,
9283             timeout: (this.form.timeout*1000),
9284             upload: this.form.fileUpload ? this.success : undefined
9285         };
9286     }
9287 };
9288
9289 Roo.form.Action.Submit = function(form, options){
9290     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9291 };
9292
9293 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9294     type : 'submit',
9295
9296     haveProgress : false,
9297     uploadComplete : false,
9298     
9299     // uploadProgress indicator.
9300     uploadProgress : function()
9301     {
9302         if (!this.form.progressUrl) {
9303             return;
9304         }
9305         
9306         if (!this.haveProgress) {
9307             Roo.MessageBox.progress("Uploading", "Uploading");
9308         }
9309         if (this.uploadComplete) {
9310            Roo.MessageBox.hide();
9311            return;
9312         }
9313         
9314         this.haveProgress = true;
9315    
9316         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9317         
9318         var c = new Roo.data.Connection();
9319         c.request({
9320             url : this.form.progressUrl,
9321             params: {
9322                 id : uid
9323             },
9324             method: 'GET',
9325             success : function(req){
9326                //console.log(data);
9327                 var rdata = false;
9328                 var edata;
9329                 try  {
9330                    rdata = Roo.decode(req.responseText)
9331                 } catch (e) {
9332                     Roo.log("Invalid data from server..");
9333                     Roo.log(edata);
9334                     return;
9335                 }
9336                 if (!rdata || !rdata.success) {
9337                     Roo.log(rdata);
9338                     Roo.MessageBox.alert(Roo.encode(rdata));
9339                     return;
9340                 }
9341                 var data = rdata.data;
9342                 
9343                 if (this.uploadComplete) {
9344                    Roo.MessageBox.hide();
9345                    return;
9346                 }
9347                    
9348                 if (data){
9349                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9350                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9351                     );
9352                 }
9353                 this.uploadProgress.defer(2000,this);
9354             },
9355        
9356             failure: function(data) {
9357                 Roo.log('progress url failed ');
9358                 Roo.log(data);
9359             },
9360             scope : this
9361         });
9362            
9363     },
9364     
9365     
9366     run : function()
9367     {
9368         // run get Values on the form, so it syncs any secondary forms.
9369         this.form.getValues();
9370         
9371         var o = this.options;
9372         var method = this.getMethod();
9373         var isPost = method == 'POST';
9374         if(o.clientValidation === false || this.form.isValid()){
9375             
9376             if (this.form.progressUrl) {
9377                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9378                     (new Date() * 1) + '' + Math.random());
9379                     
9380             } 
9381             
9382             
9383             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9384                 form:this.form.el.dom,
9385                 url:this.getUrl(!isPost),
9386                 method: method,
9387                 params:isPost ? this.getParams() : null,
9388                 isUpload: this.form.fileUpload,
9389                 formData : this.form.formData
9390             }));
9391             
9392             this.uploadProgress();
9393
9394         }else if (o.clientValidation !== false){ // client validation failed
9395             this.failureType = Roo.form.Action.CLIENT_INVALID;
9396             this.form.afterAction(this, false);
9397         }
9398     },
9399
9400     success : function(response)
9401     {
9402         this.uploadComplete= true;
9403         if (this.haveProgress) {
9404             Roo.MessageBox.hide();
9405         }
9406         
9407         
9408         var result = this.processResponse(response);
9409         if(result === true || result.success){
9410             this.form.afterAction(this, true);
9411             return;
9412         }
9413         if(result.errors){
9414             this.form.markInvalid(result.errors);
9415             this.failureType = Roo.form.Action.SERVER_INVALID;
9416         }
9417         this.form.afterAction(this, false);
9418     },
9419     failure : function(response)
9420     {
9421         this.uploadComplete= true;
9422         if (this.haveProgress) {
9423             Roo.MessageBox.hide();
9424         }
9425         
9426         this.response = response;
9427         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9428         this.form.afterAction(this, false);
9429     },
9430     
9431     handleResponse : function(response){
9432         if(this.form.errorReader){
9433             var rs = this.form.errorReader.read(response);
9434             var errors = [];
9435             if(rs.records){
9436                 for(var i = 0, len = rs.records.length; i < len; i++) {
9437                     var r = rs.records[i];
9438                     errors[i] = r.data;
9439                 }
9440             }
9441             if(errors.length < 1){
9442                 errors = null;
9443             }
9444             return {
9445                 success : rs.success,
9446                 errors : errors
9447             };
9448         }
9449         var ret = false;
9450         try {
9451             ret = Roo.decode(response.responseText);
9452         } catch (e) {
9453             ret = {
9454                 success: false,
9455                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9456                 errors : []
9457             };
9458         }
9459         return ret;
9460         
9461     }
9462 });
9463
9464
9465 Roo.form.Action.Load = function(form, options){
9466     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9467     this.reader = this.form.reader;
9468 };
9469
9470 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9471     type : 'load',
9472
9473     run : function(){
9474         
9475         Roo.Ajax.request(Roo.apply(
9476                 this.createCallback(), {
9477                     method:this.getMethod(),
9478                     url:this.getUrl(false),
9479                     params:this.getParams()
9480         }));
9481     },
9482
9483     success : function(response){
9484         
9485         var result = this.processResponse(response);
9486         if(result === true || !result.success || !result.data){
9487             this.failureType = Roo.form.Action.LOAD_FAILURE;
9488             this.form.afterAction(this, false);
9489             return;
9490         }
9491         this.form.clearInvalid();
9492         this.form.setValues(result.data);
9493         this.form.afterAction(this, true);
9494     },
9495
9496     handleResponse : function(response){
9497         if(this.form.reader){
9498             var rs = this.form.reader.read(response);
9499             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9500             return {
9501                 success : rs.success,
9502                 data : data
9503             };
9504         }
9505         return Roo.decode(response.responseText);
9506     }
9507 });
9508
9509 Roo.form.Action.ACTION_TYPES = {
9510     'load' : Roo.form.Action.Load,
9511     'submit' : Roo.form.Action.Submit
9512 };/*
9513  * - LGPL
9514  *
9515  * form
9516  *
9517  */
9518
9519 /**
9520  * @class Roo.bootstrap.Form
9521  * @extends Roo.bootstrap.Component
9522  * Bootstrap Form class
9523  * @cfg {String} method  GET | POST (default POST)
9524  * @cfg {String} labelAlign top | left (default top)
9525  * @cfg {String} align left  | right - for navbars
9526  * @cfg {Boolean} loadMask load mask when submit (default true)
9527
9528  *
9529  * @constructor
9530  * Create a new Form
9531  * @param {Object} config The config object
9532  */
9533
9534
9535 Roo.bootstrap.Form = function(config){
9536     
9537     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9538     
9539     Roo.bootstrap.Form.popover.apply();
9540     
9541     this.addEvents({
9542         /**
9543          * @event clientvalidation
9544          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9545          * @param {Form} this
9546          * @param {Boolean} valid true if the form has passed client-side validation
9547          */
9548         clientvalidation: true,
9549         /**
9550          * @event beforeaction
9551          * Fires before any action is performed. Return false to cancel the action.
9552          * @param {Form} this
9553          * @param {Action} action The action to be performed
9554          */
9555         beforeaction: true,
9556         /**
9557          * @event actionfailed
9558          * Fires when an action fails.
9559          * @param {Form} this
9560          * @param {Action} action The action that failed
9561          */
9562         actionfailed : true,
9563         /**
9564          * @event actioncomplete
9565          * Fires when an action is completed.
9566          * @param {Form} this
9567          * @param {Action} action The action that completed
9568          */
9569         actioncomplete : true
9570     });
9571 };
9572
9573 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9574
9575      /**
9576      * @cfg {String} method
9577      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9578      */
9579     method : 'POST',
9580     /**
9581      * @cfg {String} url
9582      * The URL to use for form actions if one isn't supplied in the action options.
9583      */
9584     /**
9585      * @cfg {Boolean} fileUpload
9586      * Set to true if this form is a file upload.
9587      */
9588
9589     /**
9590      * @cfg {Object} baseParams
9591      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9592      */
9593
9594     /**
9595      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9596      */
9597     timeout: 30,
9598     /**
9599      * @cfg {Sting} align (left|right) for navbar forms
9600      */
9601     align : 'left',
9602
9603     // private
9604     activeAction : null,
9605
9606     /**
9607      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9608      * element by passing it or its id or mask the form itself by passing in true.
9609      * @type Mixed
9610      */
9611     waitMsgTarget : false,
9612
9613     loadMask : true,
9614     
9615     /**
9616      * @cfg {Boolean} errorMask (true|false) default false
9617      */
9618     errorMask : false,
9619     
9620     /**
9621      * @cfg {Number} maskOffset Default 100
9622      */
9623     maskOffset : 100,
9624     
9625     /**
9626      * @cfg {Boolean} maskBody
9627      */
9628     maskBody : false,
9629
9630     getAutoCreate : function(){
9631
9632         var cfg = {
9633             tag: 'form',
9634             method : this.method || 'POST',
9635             id : this.id || Roo.id(),
9636             cls : ''
9637         };
9638         if (this.parent().xtype.match(/^Nav/)) {
9639             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9640
9641         }
9642
9643         if (this.labelAlign == 'left' ) {
9644             cfg.cls += ' form-horizontal';
9645         }
9646
9647
9648         return cfg;
9649     },
9650     initEvents : function()
9651     {
9652         this.el.on('submit', this.onSubmit, this);
9653         // this was added as random key presses on the form where triggering form submit.
9654         this.el.on('keypress', function(e) {
9655             if (e.getCharCode() != 13) {
9656                 return true;
9657             }
9658             // we might need to allow it for textareas.. and some other items.
9659             // check e.getTarget().
9660
9661             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9662                 return true;
9663             }
9664
9665             Roo.log("keypress blocked");
9666
9667             e.preventDefault();
9668             return false;
9669         });
9670         
9671     },
9672     // private
9673     onSubmit : function(e){
9674         e.stopEvent();
9675     },
9676
9677      /**
9678      * Returns true if client-side validation on the form is successful.
9679      * @return Boolean
9680      */
9681     isValid : function(){
9682         var items = this.getItems();
9683         var valid = true;
9684         var target = false;
9685         
9686         items.each(function(f){
9687             
9688             if(f.validate()){
9689                 return;
9690             }
9691             
9692             Roo.log('invalid field: ' + f.name);
9693             
9694             valid = false;
9695
9696             if(!target && f.el.isVisible(true)){
9697                 target = f;
9698             }
9699            
9700         });
9701         
9702         if(this.errorMask && !valid){
9703             Roo.bootstrap.Form.popover.mask(this, target);
9704         }
9705         
9706         return valid;
9707     },
9708     
9709     /**
9710      * Returns true if any fields in this form have changed since their original load.
9711      * @return Boolean
9712      */
9713     isDirty : function(){
9714         var dirty = false;
9715         var items = this.getItems();
9716         items.each(function(f){
9717            if(f.isDirty()){
9718                dirty = true;
9719                return false;
9720            }
9721            return true;
9722         });
9723         return dirty;
9724     },
9725      /**
9726      * Performs a predefined action (submit or load) or custom actions you define on this form.
9727      * @param {String} actionName The name of the action type
9728      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9729      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9730      * accept other config options):
9731      * <pre>
9732 Property          Type             Description
9733 ----------------  ---------------  ----------------------------------------------------------------------------------
9734 url               String           The url for the action (defaults to the form's url)
9735 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9736 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9737 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9738                                    validate the form on the client (defaults to false)
9739      * </pre>
9740      * @return {BasicForm} this
9741      */
9742     doAction : function(action, options){
9743         if(typeof action == 'string'){
9744             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9745         }
9746         if(this.fireEvent('beforeaction', this, action) !== false){
9747             this.beforeAction(action);
9748             action.run.defer(100, action);
9749         }
9750         return this;
9751     },
9752
9753     // private
9754     beforeAction : function(action){
9755         var o = action.options;
9756         
9757         if(this.loadMask){
9758             
9759             if(this.maskBody){
9760                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9761             } else {
9762                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9763             }
9764         }
9765         // not really supported yet.. ??
9766
9767         //if(this.waitMsgTarget === true){
9768         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else if(this.waitMsgTarget){
9770         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9771         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9772         //}else {
9773         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9774        // }
9775
9776     },
9777
9778     // private
9779     afterAction : function(action, success){
9780         this.activeAction = null;
9781         var o = action.options;
9782
9783         if(this.loadMask){
9784             
9785             if(this.maskBody){
9786                 Roo.get(document.body).unmask();
9787             } else {
9788                 this.el.unmask();
9789             }
9790         }
9791         
9792         //if(this.waitMsgTarget === true){
9793 //            this.el.unmask();
9794         //}else if(this.waitMsgTarget){
9795         //    this.waitMsgTarget.unmask();
9796         //}else{
9797         //    Roo.MessageBox.updateProgress(1);
9798         //    Roo.MessageBox.hide();
9799        // }
9800         //
9801         if(success){
9802             if(o.reset){
9803                 this.reset();
9804             }
9805             Roo.callback(o.success, o.scope, [this, action]);
9806             this.fireEvent('actioncomplete', this, action);
9807
9808         }else{
9809
9810             // failure condition..
9811             // we have a scenario where updates need confirming.
9812             // eg. if a locking scenario exists..
9813             // we look for { errors : { needs_confirm : true }} in the response.
9814             if (
9815                 (typeof(action.result) != 'undefined')  &&
9816                 (typeof(action.result.errors) != 'undefined')  &&
9817                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9818            ){
9819                 var _t = this;
9820                 Roo.log("not supported yet");
9821                  /*
9822
9823                 Roo.MessageBox.confirm(
9824                     "Change requires confirmation",
9825                     action.result.errorMsg,
9826                     function(r) {
9827                         if (r != 'yes') {
9828                             return;
9829                         }
9830                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9831                     }
9832
9833                 );
9834                 */
9835
9836
9837                 return;
9838             }
9839
9840             Roo.callback(o.failure, o.scope, [this, action]);
9841             // show an error message if no failed handler is set..
9842             if (!this.hasListener('actionfailed')) {
9843                 Roo.log("need to add dialog support");
9844                 /*
9845                 Roo.MessageBox.alert("Error",
9846                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9847                         action.result.errorMsg :
9848                         "Saving Failed, please check your entries or try again"
9849                 );
9850                 */
9851             }
9852
9853             this.fireEvent('actionfailed', this, action);
9854         }
9855
9856     },
9857     /**
9858      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9859      * @param {String} id The value to search for
9860      * @return Field
9861      */
9862     findField : function(id){
9863         var items = this.getItems();
9864         var field = items.get(id);
9865         if(!field){
9866              items.each(function(f){
9867                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9868                     field = f;
9869                     return false;
9870                 }
9871                 return true;
9872             });
9873         }
9874         return field || null;
9875     },
9876      /**
9877      * Mark fields in this form invalid in bulk.
9878      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9879      * @return {BasicForm} this
9880      */
9881     markInvalid : function(errors){
9882         if(errors instanceof Array){
9883             for(var i = 0, len = errors.length; i < len; i++){
9884                 var fieldError = errors[i];
9885                 var f = this.findField(fieldError.id);
9886                 if(f){
9887                     f.markInvalid(fieldError.msg);
9888                 }
9889             }
9890         }else{
9891             var field, id;
9892             for(id in errors){
9893                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9894                     field.markInvalid(errors[id]);
9895                 }
9896             }
9897         }
9898         //Roo.each(this.childForms || [], function (f) {
9899         //    f.markInvalid(errors);
9900         //});
9901
9902         return this;
9903     },
9904
9905     /**
9906      * Set values for fields in this form in bulk.
9907      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9908      * @return {BasicForm} this
9909      */
9910     setValues : function(values){
9911         if(values instanceof Array){ // array of objects
9912             for(var i = 0, len = values.length; i < len; i++){
9913                 var v = values[i];
9914                 var f = this.findField(v.id);
9915                 if(f){
9916                     f.setValue(v.value);
9917                     if(this.trackResetOnLoad){
9918                         f.originalValue = f.getValue();
9919                     }
9920                 }
9921             }
9922         }else{ // object hash
9923             var field, id;
9924             for(id in values){
9925                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9926
9927                     if (field.setFromData &&
9928                         field.valueField &&
9929                         field.displayField &&
9930                         // combos' with local stores can
9931                         // be queried via setValue()
9932                         // to set their value..
9933                         (field.store && !field.store.isLocal)
9934                         ) {
9935                         // it's a combo
9936                         var sd = { };
9937                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9938                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9939                         field.setFromData(sd);
9940
9941                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9942                         
9943                         field.setFromData(values);
9944                         
9945                     } else {
9946                         field.setValue(values[id]);
9947                     }
9948
9949
9950                     if(this.trackResetOnLoad){
9951                         field.originalValue = field.getValue();
9952                     }
9953                 }
9954             }
9955         }
9956
9957         //Roo.each(this.childForms || [], function (f) {
9958         //    f.setValues(values);
9959         //});
9960
9961         return this;
9962     },
9963
9964     /**
9965      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9966      * they are returned as an array.
9967      * @param {Boolean} asString
9968      * @return {Object}
9969      */
9970     getValues : function(asString){
9971         //if (this.childForms) {
9972             // copy values from the child forms
9973         //    Roo.each(this.childForms, function (f) {
9974         //        this.setValues(f.getValues());
9975         //    }, this);
9976         //}
9977
9978
9979
9980         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9981         if(asString === true){
9982             return fs;
9983         }
9984         return Roo.urlDecode(fs);
9985     },
9986
9987     /**
9988      * Returns the fields in this form as an object with key/value pairs.
9989      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9990      * @return {Object}
9991      */
9992     getFieldValues : function(with_hidden)
9993     {
9994         var items = this.getItems();
9995         var ret = {};
9996         items.each(function(f){
9997             
9998             if (!f.getName()) {
9999                 return;
10000             }
10001             
10002             var v = f.getValue();
10003             
10004             if (f.inputType =='radio') {
10005                 if (typeof(ret[f.getName()]) == 'undefined') {
10006                     ret[f.getName()] = ''; // empty..
10007                 }
10008
10009                 if (!f.el.dom.checked) {
10010                     return;
10011
10012                 }
10013                 v = f.el.dom.value;
10014
10015             }
10016             
10017             if(f.xtype == 'MoneyField'){
10018                 ret[f.currencyName] = f.getCurrency();
10019             }
10020
10021             // not sure if this supported any more..
10022             if ((typeof(v) == 'object') && f.getRawValue) {
10023                 v = f.getRawValue() ; // dates..
10024             }
10025             // combo boxes where name != hiddenName...
10026             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10027                 ret[f.name] = f.getRawValue();
10028             }
10029             ret[f.getName()] = v;
10030         });
10031
10032         return ret;
10033     },
10034
10035     /**
10036      * Clears all invalid messages in this form.
10037      * @return {BasicForm} this
10038      */
10039     clearInvalid : function(){
10040         var items = this.getItems();
10041
10042         items.each(function(f){
10043            f.clearInvalid();
10044         });
10045
10046         return this;
10047     },
10048
10049     /**
10050      * Resets this form.
10051      * @return {BasicForm} this
10052      */
10053     reset : function(){
10054         var items = this.getItems();
10055         items.each(function(f){
10056             f.reset();
10057         });
10058
10059         Roo.each(this.childForms || [], function (f) {
10060             f.reset();
10061         });
10062
10063
10064         return this;
10065     },
10066     
10067     getItems : function()
10068     {
10069         var r=new Roo.util.MixedCollection(false, function(o){
10070             return o.id || (o.id = Roo.id());
10071         });
10072         var iter = function(el) {
10073             if (el.inputEl) {
10074                 r.add(el);
10075             }
10076             if (!el.items) {
10077                 return;
10078             }
10079             Roo.each(el.items,function(e) {
10080                 iter(e);
10081             });
10082         };
10083
10084         iter(this);
10085         return r;
10086     },
10087     
10088     hideFields : function(items)
10089     {
10090         Roo.each(items, function(i){
10091             
10092             var f = this.findField(i);
10093             
10094             if(!f){
10095                 return;
10096             }
10097             
10098             f.hide();
10099             
10100         }, this);
10101     },
10102     
10103     showFields : function(items)
10104     {
10105         Roo.each(items, function(i){
10106             
10107             var f = this.findField(i);
10108             
10109             if(!f){
10110                 return;
10111             }
10112             
10113             f.show();
10114             
10115         }, this);
10116     }
10117
10118 });
10119
10120 Roo.apply(Roo.bootstrap.Form, {
10121     
10122     popover : {
10123         
10124         padding : 5,
10125         
10126         isApplied : false,
10127         
10128         isMasked : false,
10129         
10130         form : false,
10131         
10132         target : false,
10133         
10134         toolTip : false,
10135         
10136         intervalID : false,
10137         
10138         maskEl : false,
10139         
10140         apply : function()
10141         {
10142             if(this.isApplied){
10143                 return;
10144             }
10145             
10146             this.maskEl = {
10147                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10148                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10149                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10150                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10151             };
10152             
10153             this.maskEl.top.enableDisplayMode("block");
10154             this.maskEl.left.enableDisplayMode("block");
10155             this.maskEl.bottom.enableDisplayMode("block");
10156             this.maskEl.right.enableDisplayMode("block");
10157             
10158             this.toolTip = new Roo.bootstrap.Tooltip({
10159                 cls : 'roo-form-error-popover',
10160                 alignment : {
10161                     'left' : ['r-l', [-2,0], 'right'],
10162                     'right' : ['l-r', [2,0], 'left'],
10163                     'bottom' : ['tl-bl', [0,2], 'top'],
10164                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10165                 }
10166             });
10167             
10168             this.toolTip.render(Roo.get(document.body));
10169
10170             this.toolTip.el.enableDisplayMode("block");
10171             
10172             Roo.get(document.body).on('click', function(){
10173                 this.unmask();
10174             }, this);
10175             
10176             Roo.get(document.body).on('touchstart', function(){
10177                 this.unmask();
10178             }, this);
10179             
10180             this.isApplied = true
10181         },
10182         
10183         mask : function(form, target)
10184         {
10185             this.form = form;
10186             
10187             this.target = target;
10188             
10189             if(!this.form.errorMask || !target.el){
10190                 return;
10191             }
10192             
10193             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10194             
10195             Roo.log(scrollable);
10196             
10197             var ot = this.target.el.calcOffsetsTo(scrollable);
10198             
10199             var scrollTo = ot[1] - this.form.maskOffset;
10200             
10201             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10202             
10203             scrollable.scrollTo('top', scrollTo);
10204             
10205             var box = this.target.el.getBox();
10206             Roo.log(box);
10207             var zIndex = Roo.bootstrap.Modal.zIndex++;
10208
10209             
10210             this.maskEl.top.setStyle('position', 'absolute');
10211             this.maskEl.top.setStyle('z-index', zIndex);
10212             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10213             this.maskEl.top.setLeft(0);
10214             this.maskEl.top.setTop(0);
10215             this.maskEl.top.show();
10216             
10217             this.maskEl.left.setStyle('position', 'absolute');
10218             this.maskEl.left.setStyle('z-index', zIndex);
10219             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10220             this.maskEl.left.setLeft(0);
10221             this.maskEl.left.setTop(box.y - this.padding);
10222             this.maskEl.left.show();
10223
10224             this.maskEl.bottom.setStyle('position', 'absolute');
10225             this.maskEl.bottom.setStyle('z-index', zIndex);
10226             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10227             this.maskEl.bottom.setLeft(0);
10228             this.maskEl.bottom.setTop(box.bottom + this.padding);
10229             this.maskEl.bottom.show();
10230
10231             this.maskEl.right.setStyle('position', 'absolute');
10232             this.maskEl.right.setStyle('z-index', zIndex);
10233             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10234             this.maskEl.right.setLeft(box.right + this.padding);
10235             this.maskEl.right.setTop(box.y - this.padding);
10236             this.maskEl.right.show();
10237
10238             this.toolTip.bindEl = this.target.el;
10239
10240             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10241
10242             var tip = this.target.blankText;
10243
10244             if(this.target.getValue() !== '' ) {
10245                 
10246                 if (this.target.invalidText.length) {
10247                     tip = this.target.invalidText;
10248                 } else if (this.target.regexText.length){
10249                     tip = this.target.regexText;
10250                 }
10251             }
10252
10253             this.toolTip.show(tip);
10254
10255             this.intervalID = window.setInterval(function() {
10256                 Roo.bootstrap.Form.popover.unmask();
10257             }, 10000);
10258
10259             window.onwheel = function(){ return false;};
10260             
10261             (function(){ this.isMasked = true; }).defer(500, this);
10262             
10263         },
10264         
10265         unmask : function()
10266         {
10267             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10268                 return;
10269             }
10270             
10271             this.maskEl.top.setStyle('position', 'absolute');
10272             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10273             this.maskEl.top.hide();
10274
10275             this.maskEl.left.setStyle('position', 'absolute');
10276             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10277             this.maskEl.left.hide();
10278
10279             this.maskEl.bottom.setStyle('position', 'absolute');
10280             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10281             this.maskEl.bottom.hide();
10282
10283             this.maskEl.right.setStyle('position', 'absolute');
10284             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10285             this.maskEl.right.hide();
10286             
10287             this.toolTip.hide();
10288             
10289             this.toolTip.el.hide();
10290             
10291             window.onwheel = function(){ return true;};
10292             
10293             if(this.intervalID){
10294                 window.clearInterval(this.intervalID);
10295                 this.intervalID = false;
10296             }
10297             
10298             this.isMasked = false;
10299             
10300         }
10301         
10302     }
10303     
10304 });
10305
10306 /*
10307  * Based on:
10308  * Ext JS Library 1.1.1
10309  * Copyright(c) 2006-2007, Ext JS, LLC.
10310  *
10311  * Originally Released Under LGPL - original licence link has changed is not relivant.
10312  *
10313  * Fork - LGPL
10314  * <script type="text/javascript">
10315  */
10316 /**
10317  * @class Roo.form.VTypes
10318  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10319  * @singleton
10320  */
10321 Roo.form.VTypes = function(){
10322     // closure these in so they are only created once.
10323     var alpha = /^[a-zA-Z_]+$/;
10324     var alphanum = /^[a-zA-Z0-9_]+$/;
10325     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10326     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10327
10328     // All these messages and functions are configurable
10329     return {
10330         /**
10331          * The function used to validate email addresses
10332          * @param {String} value The email address
10333          */
10334         'email' : function(v){
10335             return email.test(v);
10336         },
10337         /**
10338          * The error text to display when the email validation function returns false
10339          * @type String
10340          */
10341         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10342         /**
10343          * The keystroke filter mask to be applied on email input
10344          * @type RegExp
10345          */
10346         'emailMask' : /[a-z0-9_\.\-@]/i,
10347
10348         /**
10349          * The function used to validate URLs
10350          * @param {String} value The URL
10351          */
10352         'url' : function(v){
10353             return url.test(v);
10354         },
10355         /**
10356          * The error text to display when the url validation function returns false
10357          * @type String
10358          */
10359         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10360         
10361         /**
10362          * The function used to validate alpha values
10363          * @param {String} value The value
10364          */
10365         'alpha' : function(v){
10366             return alpha.test(v);
10367         },
10368         /**
10369          * The error text to display when the alpha validation function returns false
10370          * @type String
10371          */
10372         'alphaText' : 'This field should only contain letters and _',
10373         /**
10374          * The keystroke filter mask to be applied on alpha input
10375          * @type RegExp
10376          */
10377         'alphaMask' : /[a-z_]/i,
10378
10379         /**
10380          * The function used to validate alphanumeric values
10381          * @param {String} value The value
10382          */
10383         'alphanum' : function(v){
10384             return alphanum.test(v);
10385         },
10386         /**
10387          * The error text to display when the alphanumeric validation function returns false
10388          * @type String
10389          */
10390         'alphanumText' : 'This field should only contain letters, numbers and _',
10391         /**
10392          * The keystroke filter mask to be applied on alphanumeric input
10393          * @type RegExp
10394          */
10395         'alphanumMask' : /[a-z0-9_]/i
10396     };
10397 }();/*
10398  * - LGPL
10399  *
10400  * Input
10401  * 
10402  */
10403
10404 /**
10405  * @class Roo.bootstrap.Input
10406  * @extends Roo.bootstrap.Component
10407  * Bootstrap Input class
10408  * @cfg {Boolean} disabled is it disabled
10409  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10410  * @cfg {String} name name of the input
10411  * @cfg {string} fieldLabel - the label associated
10412  * @cfg {string} placeholder - placeholder to put in text.
10413  * @cfg {string}  before - input group add on before
10414  * @cfg {string} after - input group add on after
10415  * @cfg {string} size - (lg|sm) or leave empty..
10416  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10417  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10418  * @cfg {Number} md colspan out of 12 for computer-sized screens
10419  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10420  * @cfg {string} value default value of the input
10421  * @cfg {Number} labelWidth set the width of label 
10422  * @cfg {Number} labellg set the width of label (1-12)
10423  * @cfg {Number} labelmd set the width of label (1-12)
10424  * @cfg {Number} labelsm set the width of label (1-12)
10425  * @cfg {Number} labelxs set the width of label (1-12)
10426  * @cfg {String} labelAlign (top|left)
10427  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10428  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10429  * @cfg {String} indicatorpos (left|right) default left
10430  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10431  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10432  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10433
10434  * @cfg {String} align (left|center|right) Default left
10435  * @cfg {Boolean} forceFeedback (true|false) Default false
10436  * 
10437  * @constructor
10438  * Create a new Input
10439  * @param {Object} config The config object
10440  */
10441
10442 Roo.bootstrap.Input = function(config){
10443     
10444     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10445     
10446     this.addEvents({
10447         /**
10448          * @event focus
10449          * Fires when this field receives input focus.
10450          * @param {Roo.form.Field} this
10451          */
10452         focus : true,
10453         /**
10454          * @event blur
10455          * Fires when this field loses input focus.
10456          * @param {Roo.form.Field} this
10457          */
10458         blur : true,
10459         /**
10460          * @event specialkey
10461          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10462          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10463          * @param {Roo.form.Field} this
10464          * @param {Roo.EventObject} e The event object
10465          */
10466         specialkey : true,
10467         /**
10468          * @event change
10469          * Fires just before the field blurs if the field value has changed.
10470          * @param {Roo.form.Field} this
10471          * @param {Mixed} newValue The new value
10472          * @param {Mixed} oldValue The original value
10473          */
10474         change : true,
10475         /**
10476          * @event invalid
10477          * Fires after the field has been marked as invalid.
10478          * @param {Roo.form.Field} this
10479          * @param {String} msg The validation message
10480          */
10481         invalid : true,
10482         /**
10483          * @event valid
10484          * Fires after the field has been validated with no errors.
10485          * @param {Roo.form.Field} this
10486          */
10487         valid : true,
10488          /**
10489          * @event keyup
10490          * Fires after the key up
10491          * @param {Roo.form.Field} this
10492          * @param {Roo.EventObject}  e The event Object
10493          */
10494         keyup : true
10495     });
10496 };
10497
10498 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10499      /**
10500      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10501       automatic validation (defaults to "keyup").
10502      */
10503     validationEvent : "keyup",
10504      /**
10505      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10506      */
10507     validateOnBlur : true,
10508     /**
10509      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10510      */
10511     validationDelay : 250,
10512      /**
10513      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10514      */
10515     focusClass : "x-form-focus",  // not needed???
10516     
10517        
10518     /**
10519      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10520      */
10521     invalidClass : "has-warning",
10522     
10523     /**
10524      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525      */
10526     validClass : "has-success",
10527     
10528     /**
10529      * @cfg {Boolean} hasFeedback (true|false) default true
10530      */
10531     hasFeedback : true,
10532     
10533     /**
10534      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10535      */
10536     invalidFeedbackClass : "glyphicon-warning-sign",
10537     
10538     /**
10539      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540      */
10541     validFeedbackClass : "glyphicon-ok",
10542     
10543     /**
10544      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10545      */
10546     selectOnFocus : false,
10547     
10548      /**
10549      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10550      */
10551     maskRe : null,
10552        /**
10553      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10554      */
10555     vtype : null,
10556     
10557       /**
10558      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10559      */
10560     disableKeyFilter : false,
10561     
10562        /**
10563      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10564      */
10565     disabled : false,
10566      /**
10567      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10568      */
10569     allowBlank : true,
10570     /**
10571      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10572      */
10573     blankText : "Please complete this mandatory field",
10574     
10575      /**
10576      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10577      */
10578     minLength : 0,
10579     /**
10580      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10581      */
10582     maxLength : Number.MAX_VALUE,
10583     /**
10584      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10585      */
10586     minLengthText : "The minimum length for this field is {0}",
10587     /**
10588      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10589      */
10590     maxLengthText : "The maximum length for this field is {0}",
10591   
10592     
10593     /**
10594      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10595      * If available, this function will be called only after the basic validators all return true, and will be passed the
10596      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10597      */
10598     validator : null,
10599     /**
10600      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10601      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10602      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10603      */
10604     regex : null,
10605     /**
10606      * @cfg {String} regexText -- Depricated - use Invalid Text
10607      */
10608     regexText : "",
10609     
10610     /**
10611      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10612      */
10613     invalidText : "",
10614     
10615     
10616     
10617     autocomplete: false,
10618     
10619     
10620     fieldLabel : '',
10621     inputType : 'text',
10622     
10623     name : false,
10624     placeholder: false,
10625     before : false,
10626     after : false,
10627     size : false,
10628     hasFocus : false,
10629     preventMark: false,
10630     isFormField : true,
10631     value : '',
10632     labelWidth : 2,
10633     labelAlign : false,
10634     readOnly : false,
10635     align : false,
10636     formatedValue : false,
10637     forceFeedback : false,
10638     
10639     indicatorpos : 'left',
10640     
10641     labellg : 0,
10642     labelmd : 0,
10643     labelsm : 0,
10644     labelxs : 0,
10645     
10646     capture : '',
10647     accept : '',
10648     
10649     parentLabelAlign : function()
10650     {
10651         var parent = this;
10652         while (parent.parent()) {
10653             parent = parent.parent();
10654             if (typeof(parent.labelAlign) !='undefined') {
10655                 return parent.labelAlign;
10656             }
10657         }
10658         return 'left';
10659         
10660     },
10661     
10662     getAutoCreate : function()
10663     {
10664         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10665         
10666         var id = Roo.id();
10667         
10668         var cfg = {};
10669         
10670         if(this.inputType != 'hidden'){
10671             cfg.cls = 'form-group' //input-group
10672         }
10673         
10674         var input =  {
10675             tag: 'input',
10676             id : id,
10677             type : this.inputType,
10678             value : this.value,
10679             cls : 'form-control',
10680             placeholder : this.placeholder || '',
10681             autocomplete : this.autocomplete || 'new-password'
10682         };
10683         if (this.inputType == 'file') {
10684             input.style = 'overflow:hidden'; // why not in CSS?
10685         }
10686         
10687         if(this.capture.length){
10688             input.capture = this.capture;
10689         }
10690         
10691         if(this.accept.length){
10692             input.accept = this.accept + "/*";
10693         }
10694         
10695         if(this.align){
10696             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10697         }
10698         
10699         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10700             input.maxLength = this.maxLength;
10701         }
10702         
10703         if (this.disabled) {
10704             input.disabled=true;
10705         }
10706         
10707         if (this.readOnly) {
10708             input.readonly=true;
10709         }
10710         
10711         if (this.name) {
10712             input.name = this.name;
10713         }
10714         
10715         if (this.size) {
10716             input.cls += ' input-' + this.size;
10717         }
10718         
10719         var settings=this;
10720         ['xs','sm','md','lg'].map(function(size){
10721             if (settings[size]) {
10722                 cfg.cls += ' col-' + size + '-' + settings[size];
10723             }
10724         });
10725         
10726         var inputblock = input;
10727         
10728         var feedback = {
10729             tag: 'span',
10730             cls: 'glyphicon form-control-feedback'
10731         };
10732             
10733         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10734             
10735             inputblock = {
10736                 cls : 'has-feedback',
10737                 cn :  [
10738                     input,
10739                     feedback
10740                 ] 
10741             };  
10742         }
10743         
10744         if (this.before || this.after) {
10745             
10746             inputblock = {
10747                 cls : 'input-group',
10748                 cn :  [] 
10749             };
10750             
10751             if (this.before && typeof(this.before) == 'string') {
10752                 
10753                 inputblock.cn.push({
10754                     tag :'span',
10755                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10756                     html : this.before
10757                 });
10758             }
10759             if (this.before && typeof(this.before) == 'object') {
10760                 this.before = Roo.factory(this.before);
10761                 
10762                 inputblock.cn.push({
10763                     tag :'span',
10764                     cls : 'roo-input-before input-group-prepend   input-group-' +
10765                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10766                 });
10767             }
10768             
10769             inputblock.cn.push(input);
10770             
10771             if (this.after && typeof(this.after) == 'string') {
10772                 inputblock.cn.push({
10773                     tag :'span',
10774                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10775                     html : this.after
10776                 });
10777             }
10778             if (this.after && typeof(this.after) == 'object') {
10779                 this.after = Roo.factory(this.after);
10780                 
10781                 inputblock.cn.push({
10782                     tag :'span',
10783                     cls : 'roo-input-after input-group-append  input-group-' +
10784                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10785                 });
10786             }
10787             
10788             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10789                 inputblock.cls += ' has-feedback';
10790                 inputblock.cn.push(feedback);
10791             }
10792         };
10793         var indicator = {
10794             tag : 'i',
10795             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10796             tooltip : 'This field is required'
10797         };
10798         if (this.allowBlank ) {
10799             indicator.style = this.allowBlank ? ' display:none' : '';
10800         }
10801         if (align ==='left' && this.fieldLabel.length) {
10802             
10803             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10804             
10805             cfg.cn = [
10806                 indicator,
10807                 {
10808                     tag: 'label',
10809                     'for' :  id,
10810                     cls : 'control-label col-form-label',
10811                     html : this.fieldLabel
10812
10813                 },
10814                 {
10815                     cls : "", 
10816                     cn: [
10817                         inputblock
10818                     ]
10819                 }
10820             ];
10821             
10822             var labelCfg = cfg.cn[1];
10823             var contentCfg = cfg.cn[2];
10824             
10825             if(this.indicatorpos == 'right'){
10826                 cfg.cn = [
10827                     {
10828                         tag: 'label',
10829                         'for' :  id,
10830                         cls : 'control-label col-form-label',
10831                         cn : [
10832                             {
10833                                 tag : 'span',
10834                                 html : this.fieldLabel
10835                             },
10836                             indicator
10837                         ]
10838                     },
10839                     {
10840                         cls : "",
10841                         cn: [
10842                             inputblock
10843                         ]
10844                     }
10845
10846                 ];
10847                 
10848                 labelCfg = cfg.cn[0];
10849                 contentCfg = cfg.cn[1];
10850             
10851             }
10852             
10853             if(this.labelWidth > 12){
10854                 labelCfg.style = "width: " + this.labelWidth + 'px';
10855             }
10856             
10857             if(this.labelWidth < 13 && this.labelmd == 0){
10858                 this.labelmd = this.labelWidth;
10859             }
10860             
10861             if(this.labellg > 0){
10862                 labelCfg.cls += ' col-lg-' + this.labellg;
10863                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10864             }
10865             
10866             if(this.labelmd > 0){
10867                 labelCfg.cls += ' col-md-' + this.labelmd;
10868                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10869             }
10870             
10871             if(this.labelsm > 0){
10872                 labelCfg.cls += ' col-sm-' + this.labelsm;
10873                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10874             }
10875             
10876             if(this.labelxs > 0){
10877                 labelCfg.cls += ' col-xs-' + this.labelxs;
10878                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10879             }
10880             
10881             
10882         } else if ( this.fieldLabel.length) {
10883                 
10884             
10885             
10886             cfg.cn = [
10887                 {
10888                     tag : 'i',
10889                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10890                     tooltip : 'This field is required',
10891                     style : this.allowBlank ? ' display:none' : '' 
10892                 },
10893                 {
10894                     tag: 'label',
10895                    //cls : 'input-group-addon',
10896                     html : this.fieldLabel
10897
10898                 },
10899
10900                inputblock
10901
10902            ];
10903            
10904            if(this.indicatorpos == 'right'){
10905        
10906                 cfg.cn = [
10907                     {
10908                         tag: 'label',
10909                        //cls : 'input-group-addon',
10910                         html : this.fieldLabel
10911
10912                     },
10913                     {
10914                         tag : 'i',
10915                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10916                         tooltip : 'This field is required',
10917                         style : this.allowBlank ? ' display:none' : '' 
10918                     },
10919
10920                    inputblock
10921
10922                ];
10923
10924             }
10925
10926         } else {
10927             
10928             cfg.cn = [
10929
10930                     inputblock
10931
10932             ];
10933                 
10934                 
10935         };
10936         
10937         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10938            cfg.cls += ' navbar-form';
10939         }
10940         
10941         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10942             // on BS4 we do this only if not form 
10943             cfg.cls += ' navbar-form';
10944             cfg.tag = 'li';
10945         }
10946         
10947         return cfg;
10948         
10949     },
10950     /**
10951      * return the real input element.
10952      */
10953     inputEl: function ()
10954     {
10955         return this.el.select('input.form-control',true).first();
10956     },
10957     
10958     tooltipEl : function()
10959     {
10960         return this.inputEl();
10961     },
10962     
10963     indicatorEl : function()
10964     {
10965         if (Roo.bootstrap.version == 4) {
10966             return false; // not enabled in v4 yet.
10967         }
10968         
10969         var indicator = this.el.select('i.roo-required-indicator',true).first();
10970         
10971         if(!indicator){
10972             return false;
10973         }
10974         
10975         return indicator;
10976         
10977     },
10978     
10979     setDisabled : function(v)
10980     {
10981         var i  = this.inputEl().dom;
10982         if (!v) {
10983             i.removeAttribute('disabled');
10984             return;
10985             
10986         }
10987         i.setAttribute('disabled','true');
10988     },
10989     initEvents : function()
10990     {
10991           
10992         this.inputEl().on("keydown" , this.fireKey,  this);
10993         this.inputEl().on("focus", this.onFocus,  this);
10994         this.inputEl().on("blur", this.onBlur,  this);
10995         
10996         this.inputEl().relayEvent('keyup', this);
10997         
10998         this.indicator = this.indicatorEl();
10999         
11000         if(this.indicator){
11001             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11002         }
11003  
11004         // reference to original value for reset
11005         this.originalValue = this.getValue();
11006         //Roo.form.TextField.superclass.initEvents.call(this);
11007         if(this.validationEvent == 'keyup'){
11008             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11009             this.inputEl().on('keyup', this.filterValidation, this);
11010         }
11011         else if(this.validationEvent !== false){
11012             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11013         }
11014         
11015         if(this.selectOnFocus){
11016             this.on("focus", this.preFocus, this);
11017             
11018         }
11019         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11020             this.inputEl().on("keypress", this.filterKeys, this);
11021         } else {
11022             this.inputEl().relayEvent('keypress', this);
11023         }
11024        /* if(this.grow){
11025             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11026             this.el.on("click", this.autoSize,  this);
11027         }
11028         */
11029         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11030             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11031         }
11032         
11033         if (typeof(this.before) == 'object') {
11034             this.before.render(this.el.select('.roo-input-before',true).first());
11035         }
11036         if (typeof(this.after) == 'object') {
11037             this.after.render(this.el.select('.roo-input-after',true).first());
11038         }
11039         
11040         this.inputEl().on('change', this.onChange, this);
11041         
11042     },
11043     filterValidation : function(e){
11044         if(!e.isNavKeyPress()){
11045             this.validationTask.delay(this.validationDelay);
11046         }
11047     },
11048      /**
11049      * Validates the field value
11050      * @return {Boolean} True if the value is valid, else false
11051      */
11052     validate : function(){
11053         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11054         if(this.disabled || this.validateValue(this.getRawValue())){
11055             this.markValid();
11056             return true;
11057         }
11058         
11059         this.markInvalid();
11060         return false;
11061     },
11062     
11063     
11064     /**
11065      * Validates a value according to the field's validation rules and marks the field as invalid
11066      * if the validation fails
11067      * @param {Mixed} value The value to validate
11068      * @return {Boolean} True if the value is valid, else false
11069      */
11070     validateValue : function(value)
11071     {
11072         if(this.getVisibilityEl().hasClass('hidden')){
11073             return true;
11074         }
11075         
11076         if(value.length < 1)  { // if it's blank
11077             if(this.allowBlank){
11078                 return true;
11079             }
11080             return false;
11081         }
11082         
11083         if(value.length < this.minLength){
11084             return false;
11085         }
11086         if(value.length > this.maxLength){
11087             return false;
11088         }
11089         if(this.vtype){
11090             var vt = Roo.form.VTypes;
11091             if(!vt[this.vtype](value, this)){
11092                 return false;
11093             }
11094         }
11095         if(typeof this.validator == "function"){
11096             var msg = this.validator(value);
11097             if(msg !== true){
11098                 return false;
11099             }
11100             if (typeof(msg) == 'string') {
11101                 this.invalidText = msg;
11102             }
11103         }
11104         
11105         if(this.regex && !this.regex.test(value)){
11106             return false;
11107         }
11108         
11109         return true;
11110     },
11111     
11112      // private
11113     fireKey : function(e){
11114         //Roo.log('field ' + e.getKey());
11115         if(e.isNavKeyPress()){
11116             this.fireEvent("specialkey", this, e);
11117         }
11118     },
11119     focus : function (selectText){
11120         if(this.rendered){
11121             this.inputEl().focus();
11122             if(selectText === true){
11123                 this.inputEl().dom.select();
11124             }
11125         }
11126         return this;
11127     } ,
11128     
11129     onFocus : function(){
11130         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11131            // this.el.addClass(this.focusClass);
11132         }
11133         if(!this.hasFocus){
11134             this.hasFocus = true;
11135             this.startValue = this.getValue();
11136             this.fireEvent("focus", this);
11137         }
11138     },
11139     
11140     beforeBlur : Roo.emptyFn,
11141
11142     
11143     // private
11144     onBlur : function(){
11145         this.beforeBlur();
11146         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11147             //this.el.removeClass(this.focusClass);
11148         }
11149         this.hasFocus = false;
11150         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11151             this.validate();
11152         }
11153         var v = this.getValue();
11154         if(String(v) !== String(this.startValue)){
11155             this.fireEvent('change', this, v, this.startValue);
11156         }
11157         this.fireEvent("blur", this);
11158     },
11159     
11160     onChange : function(e)
11161     {
11162         var v = this.getValue();
11163         if(String(v) !== String(this.startValue)){
11164             this.fireEvent('change', this, v, this.startValue);
11165         }
11166         
11167     },
11168     
11169     /**
11170      * Resets the current field value to the originally loaded value and clears any validation messages
11171      */
11172     reset : function(){
11173         this.setValue(this.originalValue);
11174         this.validate();
11175     },
11176      /**
11177      * Returns the name of the field
11178      * @return {Mixed} name The name field
11179      */
11180     getName: function(){
11181         return this.name;
11182     },
11183      /**
11184      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11185      * @return {Mixed} value The field value
11186      */
11187     getValue : function(){
11188         
11189         var v = this.inputEl().getValue();
11190         
11191         return v;
11192     },
11193     /**
11194      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11195      * @return {Mixed} value The field value
11196      */
11197     getRawValue : function(){
11198         var v = this.inputEl().getValue();
11199         
11200         return v;
11201     },
11202     
11203     /**
11204      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11205      * @param {Mixed} value The value to set
11206      */
11207     setRawValue : function(v){
11208         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11209     },
11210     
11211     selectText : function(start, end){
11212         var v = this.getRawValue();
11213         if(v.length > 0){
11214             start = start === undefined ? 0 : start;
11215             end = end === undefined ? v.length : end;
11216             var d = this.inputEl().dom;
11217             if(d.setSelectionRange){
11218                 d.setSelectionRange(start, end);
11219             }else if(d.createTextRange){
11220                 var range = d.createTextRange();
11221                 range.moveStart("character", start);
11222                 range.moveEnd("character", v.length-end);
11223                 range.select();
11224             }
11225         }
11226     },
11227     
11228     /**
11229      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11230      * @param {Mixed} value The value to set
11231      */
11232     setValue : function(v){
11233         this.value = v;
11234         if(this.rendered){
11235             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11236             this.validate();
11237         }
11238     },
11239     
11240     /*
11241     processValue : function(value){
11242         if(this.stripCharsRe){
11243             var newValue = value.replace(this.stripCharsRe, '');
11244             if(newValue !== value){
11245                 this.setRawValue(newValue);
11246                 return newValue;
11247             }
11248         }
11249         return value;
11250     },
11251   */
11252     preFocus : function(){
11253         
11254         if(this.selectOnFocus){
11255             this.inputEl().dom.select();
11256         }
11257     },
11258     filterKeys : function(e){
11259         var k = e.getKey();
11260         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11261             return;
11262         }
11263         var c = e.getCharCode(), cc = String.fromCharCode(c);
11264         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11265             return;
11266         }
11267         if(!this.maskRe.test(cc)){
11268             e.stopEvent();
11269         }
11270     },
11271      /**
11272      * Clear any invalid styles/messages for this field
11273      */
11274     clearInvalid : function(){
11275         
11276         if(!this.el || this.preventMark){ // not rendered
11277             return;
11278         }
11279         
11280         
11281         this.el.removeClass([this.invalidClass, 'is-invalid']);
11282         
11283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11284             
11285             var feedback = this.el.select('.form-control-feedback', true).first();
11286             
11287             if(feedback){
11288                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11289             }
11290             
11291         }
11292         
11293         if(this.indicator){
11294             this.indicator.removeClass('visible');
11295             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11296         }
11297         
11298         this.fireEvent('valid', this);
11299     },
11300     
11301      /**
11302      * Mark this field as valid
11303      */
11304     markValid : function()
11305     {
11306         if(!this.el  || this.preventMark){ // not rendered...
11307             return;
11308         }
11309         
11310         this.el.removeClass([this.invalidClass, this.validClass]);
11311         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11312
11313         var feedback = this.el.select('.form-control-feedback', true).first();
11314             
11315         if(feedback){
11316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11317         }
11318         
11319         if(this.indicator){
11320             this.indicator.removeClass('visible');
11321             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11322         }
11323         
11324         if(this.disabled){
11325             return;
11326         }
11327         
11328            
11329         if(this.allowBlank && !this.getRawValue().length){
11330             return;
11331         }
11332         if (Roo.bootstrap.version == 3) {
11333             this.el.addClass(this.validClass);
11334         } else {
11335             this.inputEl().addClass('is-valid');
11336         }
11337
11338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11339             
11340             var feedback = this.el.select('.form-control-feedback', true).first();
11341             
11342             if(feedback){
11343                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11344                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11345             }
11346             
11347         }
11348         
11349         this.fireEvent('valid', this);
11350     },
11351     
11352      /**
11353      * Mark this field as invalid
11354      * @param {String} msg The validation message
11355      */
11356     markInvalid : function(msg)
11357     {
11358         if(!this.el  || this.preventMark){ // not rendered
11359             return;
11360         }
11361         
11362         this.el.removeClass([this.invalidClass, this.validClass]);
11363         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11364         
11365         var feedback = this.el.select('.form-control-feedback', true).first();
11366             
11367         if(feedback){
11368             this.el.select('.form-control-feedback', true).first().removeClass(
11369                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11370         }
11371
11372         if(this.disabled){
11373             return;
11374         }
11375         
11376         if(this.allowBlank && !this.getRawValue().length){
11377             return;
11378         }
11379         
11380         if(this.indicator){
11381             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11382             this.indicator.addClass('visible');
11383         }
11384         if (Roo.bootstrap.version == 3) {
11385             this.el.addClass(this.invalidClass);
11386         } else {
11387             this.inputEl().addClass('is-invalid');
11388         }
11389         
11390         
11391         
11392         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11393             
11394             var feedback = this.el.select('.form-control-feedback', true).first();
11395             
11396             if(feedback){
11397                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11398                 
11399                 if(this.getValue().length || this.forceFeedback){
11400                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11401                 }
11402                 
11403             }
11404             
11405         }
11406         
11407         this.fireEvent('invalid', this, msg);
11408     },
11409     // private
11410     SafariOnKeyDown : function(event)
11411     {
11412         // this is a workaround for a password hang bug on chrome/ webkit.
11413         if (this.inputEl().dom.type != 'password') {
11414             return;
11415         }
11416         
11417         var isSelectAll = false;
11418         
11419         if(this.inputEl().dom.selectionEnd > 0){
11420             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11421         }
11422         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11423             event.preventDefault();
11424             this.setValue('');
11425             return;
11426         }
11427         
11428         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11429             
11430             event.preventDefault();
11431             // this is very hacky as keydown always get's upper case.
11432             //
11433             var cc = String.fromCharCode(event.getCharCode());
11434             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11435             
11436         }
11437     },
11438     adjustWidth : function(tag, w){
11439         tag = tag.toLowerCase();
11440         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11441             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11442                 if(tag == 'input'){
11443                     return w + 2;
11444                 }
11445                 if(tag == 'textarea'){
11446                     return w-2;
11447                 }
11448             }else if(Roo.isOpera){
11449                 if(tag == 'input'){
11450                     return w + 2;
11451                 }
11452                 if(tag == 'textarea'){
11453                     return w-2;
11454                 }
11455             }
11456         }
11457         return w;
11458     },
11459     
11460     setFieldLabel : function(v)
11461     {
11462         if(!this.rendered){
11463             return;
11464         }
11465         
11466         if(this.indicatorEl()){
11467             var ar = this.el.select('label > span',true);
11468             
11469             if (ar.elements.length) {
11470                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11471                 this.fieldLabel = v;
11472                 return;
11473             }
11474             
11475             var br = this.el.select('label',true);
11476             
11477             if(br.elements.length) {
11478                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11479                 this.fieldLabel = v;
11480                 return;
11481             }
11482             
11483             Roo.log('Cannot Found any of label > span || label in input');
11484             return;
11485         }
11486         
11487         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11488         this.fieldLabel = v;
11489         
11490         
11491     }
11492 });
11493
11494  
11495 /*
11496  * - LGPL
11497  *
11498  * Input
11499  * 
11500  */
11501
11502 /**
11503  * @class Roo.bootstrap.TextArea
11504  * @extends Roo.bootstrap.Input
11505  * Bootstrap TextArea class
11506  * @cfg {Number} cols Specifies the visible width of a text area
11507  * @cfg {Number} rows Specifies the visible number of lines in a text area
11508  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11509  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11510  * @cfg {string} html text
11511  * 
11512  * @constructor
11513  * Create a new TextArea
11514  * @param {Object} config The config object
11515  */
11516
11517 Roo.bootstrap.TextArea = function(config){
11518     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11519    
11520 };
11521
11522 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11523      
11524     cols : false,
11525     rows : 5,
11526     readOnly : false,
11527     warp : 'soft',
11528     resize : false,
11529     value: false,
11530     html: false,
11531     
11532     getAutoCreate : function(){
11533         
11534         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11535         
11536         var id = Roo.id();
11537         
11538         var cfg = {};
11539         
11540         if(this.inputType != 'hidden'){
11541             cfg.cls = 'form-group' //input-group
11542         }
11543         
11544         var input =  {
11545             tag: 'textarea',
11546             id : id,
11547             warp : this.warp,
11548             rows : this.rows,
11549             value : this.value || '',
11550             html: this.html || '',
11551             cls : 'form-control',
11552             placeholder : this.placeholder || '' 
11553             
11554         };
11555         
11556         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11557             input.maxLength = this.maxLength;
11558         }
11559         
11560         if(this.resize){
11561             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11562         }
11563         
11564         if(this.cols){
11565             input.cols = this.cols;
11566         }
11567         
11568         if (this.readOnly) {
11569             input.readonly = true;
11570         }
11571         
11572         if (this.name) {
11573             input.name = this.name;
11574         }
11575         
11576         if (this.size) {
11577             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11578         }
11579         
11580         var settings=this;
11581         ['xs','sm','md','lg'].map(function(size){
11582             if (settings[size]) {
11583                 cfg.cls += ' col-' + size + '-' + settings[size];
11584             }
11585         });
11586         
11587         var inputblock = input;
11588         
11589         if(this.hasFeedback && !this.allowBlank){
11590             
11591             var feedback = {
11592                 tag: 'span',
11593                 cls: 'glyphicon form-control-feedback'
11594             };
11595
11596             inputblock = {
11597                 cls : 'has-feedback',
11598                 cn :  [
11599                     input,
11600                     feedback
11601                 ] 
11602             };  
11603         }
11604         
11605         
11606         if (this.before || this.after) {
11607             
11608             inputblock = {
11609                 cls : 'input-group',
11610                 cn :  [] 
11611             };
11612             if (this.before) {
11613                 inputblock.cn.push({
11614                     tag :'span',
11615                     cls : 'input-group-addon',
11616                     html : this.before
11617                 });
11618             }
11619             
11620             inputblock.cn.push(input);
11621             
11622             if(this.hasFeedback && !this.allowBlank){
11623                 inputblock.cls += ' has-feedback';
11624                 inputblock.cn.push(feedback);
11625             }
11626             
11627             if (this.after) {
11628                 inputblock.cn.push({
11629                     tag :'span',
11630                     cls : 'input-group-addon',
11631                     html : this.after
11632                 });
11633             }
11634             
11635         }
11636         
11637         if (align ==='left' && this.fieldLabel.length) {
11638             cfg.cn = [
11639                 {
11640                     tag: 'label',
11641                     'for' :  id,
11642                     cls : 'control-label',
11643                     html : this.fieldLabel
11644                 },
11645                 {
11646                     cls : "",
11647                     cn: [
11648                         inputblock
11649                     ]
11650                 }
11651
11652             ];
11653             
11654             if(this.labelWidth > 12){
11655                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11656             }
11657
11658             if(this.labelWidth < 13 && this.labelmd == 0){
11659                 this.labelmd = this.labelWidth;
11660             }
11661
11662             if(this.labellg > 0){
11663                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11664                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11665             }
11666
11667             if(this.labelmd > 0){
11668                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11669                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11670             }
11671
11672             if(this.labelsm > 0){
11673                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11674                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11675             }
11676
11677             if(this.labelxs > 0){
11678                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11679                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11680             }
11681             
11682         } else if ( this.fieldLabel.length) {
11683             cfg.cn = [
11684
11685                {
11686                    tag: 'label',
11687                    //cls : 'input-group-addon',
11688                    html : this.fieldLabel
11689
11690                },
11691
11692                inputblock
11693
11694            ];
11695
11696         } else {
11697
11698             cfg.cn = [
11699
11700                 inputblock
11701
11702             ];
11703                 
11704         }
11705         
11706         if (this.disabled) {
11707             input.disabled=true;
11708         }
11709         
11710         return cfg;
11711         
11712     },
11713     /**
11714      * return the real textarea element.
11715      */
11716     inputEl: function ()
11717     {
11718         return this.el.select('textarea.form-control',true).first();
11719     },
11720     
11721     /**
11722      * Clear any invalid styles/messages for this field
11723      */
11724     clearInvalid : function()
11725     {
11726         
11727         if(!this.el || this.preventMark){ // not rendered
11728             return;
11729         }
11730         
11731         var label = this.el.select('label', true).first();
11732         var icon = this.el.select('i.fa-star', true).first();
11733         
11734         if(label && icon){
11735             icon.remove();
11736         }
11737         this.el.removeClass( this.validClass);
11738         this.inputEl().removeClass('is-invalid');
11739          
11740         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11741             
11742             var feedback = this.el.select('.form-control-feedback', true).first();
11743             
11744             if(feedback){
11745                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11746             }
11747             
11748         }
11749         
11750         this.fireEvent('valid', this);
11751     },
11752     
11753      /**
11754      * Mark this field as valid
11755      */
11756     markValid : function()
11757     {
11758         if(!this.el  || this.preventMark){ // not rendered
11759             return;
11760         }
11761         
11762         this.el.removeClass([this.invalidClass, this.validClass]);
11763         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11764         
11765         var feedback = this.el.select('.form-control-feedback', true).first();
11766             
11767         if(feedback){
11768             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11769         }
11770
11771         if(this.disabled || this.allowBlank){
11772             return;
11773         }
11774         
11775         var label = this.el.select('label', true).first();
11776         var icon = this.el.select('i.fa-star', true).first();
11777         
11778         if(label && icon){
11779             icon.remove();
11780         }
11781         if (Roo.bootstrap.version == 3) {
11782             this.el.addClass(this.validClass);
11783         } else {
11784             this.inputEl().addClass('is-valid');
11785         }
11786         
11787         
11788         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11789             
11790             var feedback = this.el.select('.form-control-feedback', true).first();
11791             
11792             if(feedback){
11793                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11794                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11795             }
11796             
11797         }
11798         
11799         this.fireEvent('valid', this);
11800     },
11801     
11802      /**
11803      * Mark this field as invalid
11804      * @param {String} msg The validation message
11805      */
11806     markInvalid : function(msg)
11807     {
11808         if(!this.el  || this.preventMark){ // not rendered
11809             return;
11810         }
11811         
11812         this.el.removeClass([this.invalidClass, this.validClass]);
11813         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11814         
11815         var feedback = this.el.select('.form-control-feedback', true).first();
11816             
11817         if(feedback){
11818             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11819         }
11820
11821         if(this.disabled || this.allowBlank){
11822             return;
11823         }
11824         
11825         var label = this.el.select('label', true).first();
11826         var icon = this.el.select('i.fa-star', true).first();
11827         
11828         if(!this.getValue().length && label && !icon){
11829             this.el.createChild({
11830                 tag : 'i',
11831                 cls : 'text-danger fa fa-lg fa-star',
11832                 tooltip : 'This field is required',
11833                 style : 'margin-right:5px;'
11834             }, label, true);
11835         }
11836         
11837         if (Roo.bootstrap.version == 3) {
11838             this.el.addClass(this.invalidClass);
11839         } else {
11840             this.inputEl().addClass('is-invalid');
11841         }
11842         
11843         // fixme ... this may be depricated need to test..
11844         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11845             
11846             var feedback = this.el.select('.form-control-feedback', true).first();
11847             
11848             if(feedback){
11849                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11850                 
11851                 if(this.getValue().length || this.forceFeedback){
11852                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11853                 }
11854                 
11855             }
11856             
11857         }
11858         
11859         this.fireEvent('invalid', this, msg);
11860     }
11861 });
11862
11863  
11864 /*
11865  * - LGPL
11866  *
11867  * trigger field - base class for combo..
11868  * 
11869  */
11870  
11871 /**
11872  * @class Roo.bootstrap.TriggerField
11873  * @extends Roo.bootstrap.Input
11874  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11875  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11876  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11877  * for which you can provide a custom implementation.  For example:
11878  * <pre><code>
11879 var trigger = new Roo.bootstrap.TriggerField();
11880 trigger.onTriggerClick = myTriggerFn;
11881 trigger.applyTo('my-field');
11882 </code></pre>
11883  *
11884  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11885  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11886  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11887  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11888  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11889
11890  * @constructor
11891  * Create a new TriggerField.
11892  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11893  * to the base TextField)
11894  */
11895 Roo.bootstrap.TriggerField = function(config){
11896     this.mimicing = false;
11897     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11898 };
11899
11900 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11901     /**
11902      * @cfg {String} triggerClass A CSS class to apply to the trigger
11903      */
11904      /**
11905      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11906      */
11907     hideTrigger:false,
11908
11909     /**
11910      * @cfg {Boolean} removable (true|false) special filter default false
11911      */
11912     removable : false,
11913     
11914     /** @cfg {Boolean} grow @hide */
11915     /** @cfg {Number} growMin @hide */
11916     /** @cfg {Number} growMax @hide */
11917
11918     /**
11919      * @hide 
11920      * @method
11921      */
11922     autoSize: Roo.emptyFn,
11923     // private
11924     monitorTab : true,
11925     // private
11926     deferHeight : true,
11927
11928     
11929     actionMode : 'wrap',
11930     
11931     caret : false,
11932     
11933     
11934     getAutoCreate : function(){
11935        
11936         var align = this.labelAlign || this.parentLabelAlign();
11937         
11938         var id = Roo.id();
11939         
11940         var cfg = {
11941             cls: 'form-group' //input-group
11942         };
11943         
11944         
11945         var input =  {
11946             tag: 'input',
11947             id : id,
11948             type : this.inputType,
11949             cls : 'form-control',
11950             autocomplete: 'new-password',
11951             placeholder : this.placeholder || '' 
11952             
11953         };
11954         if (this.name) {
11955             input.name = this.name;
11956         }
11957         if (this.size) {
11958             input.cls += ' input-' + this.size;
11959         }
11960         
11961         if (this.disabled) {
11962             input.disabled=true;
11963         }
11964         
11965         var inputblock = input;
11966         
11967         if(this.hasFeedback && !this.allowBlank){
11968             
11969             var feedback = {
11970                 tag: 'span',
11971                 cls: 'glyphicon form-control-feedback'
11972             };
11973             
11974             if(this.removable && !this.editable  ){
11975                 inputblock = {
11976                     cls : 'has-feedback',
11977                     cn :  [
11978                         inputblock,
11979                         {
11980                             tag: 'button',
11981                             html : 'x',
11982                             cls : 'roo-combo-removable-btn close'
11983                         },
11984                         feedback
11985                     ] 
11986                 };
11987             } else {
11988                 inputblock = {
11989                     cls : 'has-feedback',
11990                     cn :  [
11991                         inputblock,
11992                         feedback
11993                     ] 
11994                 };
11995             }
11996
11997         } else {
11998             if(this.removable && !this.editable ){
11999                 inputblock = {
12000                     cls : 'roo-removable',
12001                     cn :  [
12002                         inputblock,
12003                         {
12004                             tag: 'button',
12005                             html : 'x',
12006                             cls : 'roo-combo-removable-btn close'
12007                         }
12008                     ] 
12009                 };
12010             }
12011         }
12012         
12013         if (this.before || this.after) {
12014             
12015             inputblock = {
12016                 cls : 'input-group',
12017                 cn :  [] 
12018             };
12019             if (this.before) {
12020                 inputblock.cn.push({
12021                     tag :'span',
12022                     cls : 'input-group-addon input-group-prepend input-group-text',
12023                     html : this.before
12024                 });
12025             }
12026             
12027             inputblock.cn.push(input);
12028             
12029             if(this.hasFeedback && !this.allowBlank){
12030                 inputblock.cls += ' has-feedback';
12031                 inputblock.cn.push(feedback);
12032             }
12033             
12034             if (this.after) {
12035                 inputblock.cn.push({
12036                     tag :'span',
12037                     cls : 'input-group-addon input-group-append input-group-text',
12038                     html : this.after
12039                 });
12040             }
12041             
12042         };
12043         
12044       
12045         
12046         var ibwrap = inputblock;
12047         
12048         if(this.multiple){
12049             ibwrap = {
12050                 tag: 'ul',
12051                 cls: 'roo-select2-choices',
12052                 cn:[
12053                     {
12054                         tag: 'li',
12055                         cls: 'roo-select2-search-field',
12056                         cn: [
12057
12058                             inputblock
12059                         ]
12060                     }
12061                 ]
12062             };
12063                 
12064         }
12065         
12066         var combobox = {
12067             cls: 'roo-select2-container input-group',
12068             cn: [
12069                  {
12070                     tag: 'input',
12071                     type : 'hidden',
12072                     cls: 'form-hidden-field'
12073                 },
12074                 ibwrap
12075             ]
12076         };
12077         
12078         if(!this.multiple && this.showToggleBtn){
12079             
12080             var caret = {
12081                         tag: 'span',
12082                         cls: 'caret'
12083              };
12084             if (this.caret != false) {
12085                 caret = {
12086                      tag: 'i',
12087                      cls: 'fa fa-' + this.caret
12088                 };
12089                 
12090             }
12091             
12092             combobox.cn.push({
12093                 tag :'span',
12094                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12095                 cn : [
12096                     Roo.bootstrap.version == 3 ? caret : '',
12097                     {
12098                         tag: 'span',
12099                         cls: 'combobox-clear',
12100                         cn  : [
12101                             {
12102                                 tag : 'i',
12103                                 cls: 'icon-remove'
12104                             }
12105                         ]
12106                     }
12107                 ]
12108
12109             })
12110         }
12111         
12112         if(this.multiple){
12113             combobox.cls += ' roo-select2-container-multi';
12114         }
12115          var indicator = {
12116             tag : 'i',
12117             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12118             tooltip : 'This field is required'
12119         };
12120         if (Roo.bootstrap.version == 4) {
12121             indicator = {
12122                 tag : 'i',
12123                 style : 'display:none'
12124             };
12125         }
12126         
12127         
12128         if (align ==='left' && this.fieldLabel.length) {
12129             
12130             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12131
12132             cfg.cn = [
12133                 indicator,
12134                 {
12135                     tag: 'label',
12136                     'for' :  id,
12137                     cls : 'control-label',
12138                     html : this.fieldLabel
12139
12140                 },
12141                 {
12142                     cls : "", 
12143                     cn: [
12144                         combobox
12145                     ]
12146                 }
12147
12148             ];
12149             
12150             var labelCfg = cfg.cn[1];
12151             var contentCfg = cfg.cn[2];
12152             
12153             if(this.indicatorpos == 'right'){
12154                 cfg.cn = [
12155                     {
12156                         tag: 'label',
12157                         'for' :  id,
12158                         cls : 'control-label',
12159                         cn : [
12160                             {
12161                                 tag : 'span',
12162                                 html : this.fieldLabel
12163                             },
12164                             indicator
12165                         ]
12166                     },
12167                     {
12168                         cls : "", 
12169                         cn: [
12170                             combobox
12171                         ]
12172                     }
12173
12174                 ];
12175                 
12176                 labelCfg = cfg.cn[0];
12177                 contentCfg = cfg.cn[1];
12178             }
12179             
12180             if(this.labelWidth > 12){
12181                 labelCfg.style = "width: " + this.labelWidth + 'px';
12182             }
12183             
12184             if(this.labelWidth < 13 && this.labelmd == 0){
12185                 this.labelmd = this.labelWidth;
12186             }
12187             
12188             if(this.labellg > 0){
12189                 labelCfg.cls += ' col-lg-' + this.labellg;
12190                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12191             }
12192             
12193             if(this.labelmd > 0){
12194                 labelCfg.cls += ' col-md-' + this.labelmd;
12195                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12196             }
12197             
12198             if(this.labelsm > 0){
12199                 labelCfg.cls += ' col-sm-' + this.labelsm;
12200                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12201             }
12202             
12203             if(this.labelxs > 0){
12204                 labelCfg.cls += ' col-xs-' + this.labelxs;
12205                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12206             }
12207             
12208         } else if ( this.fieldLabel.length) {
12209 //                Roo.log(" label");
12210             cfg.cn = [
12211                 indicator,
12212                {
12213                    tag: 'label',
12214                    //cls : 'input-group-addon',
12215                    html : this.fieldLabel
12216
12217                },
12218
12219                combobox
12220
12221             ];
12222             
12223             if(this.indicatorpos == 'right'){
12224                 
12225                 cfg.cn = [
12226                     {
12227                        tag: 'label',
12228                        cn : [
12229                            {
12230                                tag : 'span',
12231                                html : this.fieldLabel
12232                            },
12233                            indicator
12234                        ]
12235
12236                     },
12237                     combobox
12238
12239                 ];
12240
12241             }
12242
12243         } else {
12244             
12245 //                Roo.log(" no label && no align");
12246                 cfg = combobox
12247                      
12248                 
12249         }
12250         
12251         var settings=this;
12252         ['xs','sm','md','lg'].map(function(size){
12253             if (settings[size]) {
12254                 cfg.cls += ' col-' + size + '-' + settings[size];
12255             }
12256         });
12257         
12258         return cfg;
12259         
12260     },
12261     
12262     
12263     
12264     // private
12265     onResize : function(w, h){
12266 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12267 //        if(typeof w == 'number'){
12268 //            var x = w - this.trigger.getWidth();
12269 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12270 //            this.trigger.setStyle('left', x+'px');
12271 //        }
12272     },
12273
12274     // private
12275     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12276
12277     // private
12278     getResizeEl : function(){
12279         return this.inputEl();
12280     },
12281
12282     // private
12283     getPositionEl : function(){
12284         return this.inputEl();
12285     },
12286
12287     // private
12288     alignErrorIcon : function(){
12289         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12290     },
12291
12292     // private
12293     initEvents : function(){
12294         
12295         this.createList();
12296         
12297         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12298         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12299         if(!this.multiple && this.showToggleBtn){
12300             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12301             if(this.hideTrigger){
12302                 this.trigger.setDisplayed(false);
12303             }
12304             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12305         }
12306         
12307         if(this.multiple){
12308             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12309         }
12310         
12311         if(this.removable && !this.editable && !this.tickable){
12312             var close = this.closeTriggerEl();
12313             
12314             if(close){
12315                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12316                 close.on('click', this.removeBtnClick, this, close);
12317             }
12318         }
12319         
12320         //this.trigger.addClassOnOver('x-form-trigger-over');
12321         //this.trigger.addClassOnClick('x-form-trigger-click');
12322         
12323         //if(!this.width){
12324         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12325         //}
12326     },
12327     
12328     closeTriggerEl : function()
12329     {
12330         var close = this.el.select('.roo-combo-removable-btn', true).first();
12331         return close ? close : false;
12332     },
12333     
12334     removeBtnClick : function(e, h, el)
12335     {
12336         e.preventDefault();
12337         
12338         if(this.fireEvent("remove", this) !== false){
12339             this.reset();
12340             this.fireEvent("afterremove", this)
12341         }
12342     },
12343     
12344     createList : function()
12345     {
12346         this.list = Roo.get(document.body).createChild({
12347             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12348             cls: 'typeahead typeahead-long dropdown-menu shadow',
12349             style: 'display:none'
12350         });
12351         
12352         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12353         
12354     },
12355
12356     // private
12357     initTrigger : function(){
12358        
12359     },
12360
12361     // private
12362     onDestroy : function(){
12363         if(this.trigger){
12364             this.trigger.removeAllListeners();
12365           //  this.trigger.remove();
12366         }
12367         //if(this.wrap){
12368         //    this.wrap.remove();
12369         //}
12370         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12371     },
12372
12373     // private
12374     onFocus : function(){
12375         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12376         /*
12377         if(!this.mimicing){
12378             this.wrap.addClass('x-trigger-wrap-focus');
12379             this.mimicing = true;
12380             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12381             if(this.monitorTab){
12382                 this.el.on("keydown", this.checkTab, this);
12383             }
12384         }
12385         */
12386     },
12387
12388     // private
12389     checkTab : function(e){
12390         if(e.getKey() == e.TAB){
12391             this.triggerBlur();
12392         }
12393     },
12394
12395     // private
12396     onBlur : function(){
12397         // do nothing
12398     },
12399
12400     // private
12401     mimicBlur : function(e, t){
12402         /*
12403         if(!this.wrap.contains(t) && this.validateBlur()){
12404             this.triggerBlur();
12405         }
12406         */
12407     },
12408
12409     // private
12410     triggerBlur : function(){
12411         this.mimicing = false;
12412         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12413         if(this.monitorTab){
12414             this.el.un("keydown", this.checkTab, this);
12415         }
12416         //this.wrap.removeClass('x-trigger-wrap-focus');
12417         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12418     },
12419
12420     // private
12421     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12422     validateBlur : function(e, t){
12423         return true;
12424     },
12425
12426     // private
12427     onDisable : function(){
12428         this.inputEl().dom.disabled = true;
12429         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12430         //if(this.wrap){
12431         //    this.wrap.addClass('x-item-disabled');
12432         //}
12433     },
12434
12435     // private
12436     onEnable : function(){
12437         this.inputEl().dom.disabled = false;
12438         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12439         //if(this.wrap){
12440         //    this.el.removeClass('x-item-disabled');
12441         //}
12442     },
12443
12444     // private
12445     onShow : function(){
12446         var ae = this.getActionEl();
12447         
12448         if(ae){
12449             ae.dom.style.display = '';
12450             ae.dom.style.visibility = 'visible';
12451         }
12452     },
12453
12454     // private
12455     
12456     onHide : function(){
12457         var ae = this.getActionEl();
12458         ae.dom.style.display = 'none';
12459     },
12460
12461     /**
12462      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12463      * by an implementing function.
12464      * @method
12465      * @param {EventObject} e
12466      */
12467     onTriggerClick : Roo.emptyFn
12468 });
12469  
12470 /*
12471 * Licence: LGPL
12472 */
12473
12474 /**
12475  * @class Roo.bootstrap.CardUploader
12476  * @extends Roo.bootstrap.Button
12477  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12478  * @cfg {Number} errorTimeout default 3000
12479  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12480  * @cfg {Array}  html The button text.
12481
12482  *
12483  * @constructor
12484  * Create a new CardUploader
12485  * @param {Object} config The config object
12486  */
12487
12488 Roo.bootstrap.CardUploader = function(config){
12489     
12490  
12491     
12492     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12493     
12494     
12495     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12496         return r.data.id
12497         });
12498     
12499     
12500 };
12501
12502 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12503     
12504      
12505     errorTimeout : 3000,
12506      
12507     images : false,
12508    
12509     fileCollection : false,
12510     allowBlank : true,
12511     
12512     getAutoCreate : function()
12513     {
12514         
12515         var cfg =  {
12516             cls :'form-group' ,
12517             cn : [
12518                
12519                 {
12520                     tag: 'label',
12521                    //cls : 'input-group-addon',
12522                     html : this.fieldLabel
12523
12524                 },
12525
12526                 {
12527                     tag: 'input',
12528                     type : 'hidden',
12529                     value : this.value,
12530                     cls : 'd-none  form-control'
12531                 },
12532                 
12533                 {
12534                     tag: 'input',
12535                     multiple : 'multiple',
12536                     type : 'file',
12537                     cls : 'd-none  roo-card-upload-selector'
12538                 },
12539                 
12540                 {
12541                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12542                 },
12543                 {
12544                     cls : 'card-columns roo-card-uploader-container'
12545                 }
12546
12547             ]
12548         };
12549            
12550          
12551         return cfg;
12552     },
12553     
12554     getChildContainer : function() /// what children are added to.
12555     {
12556         return this.containerEl;
12557     },
12558    
12559     getButtonContainer : function() /// what children are added to.
12560     {
12561         return this.el.select(".roo-card-uploader-button-container").first();
12562     },
12563    
12564     initEvents : function()
12565     {
12566         
12567         Roo.bootstrap.Input.prototype.initEvents.call(this);
12568         
12569         var t = this;
12570         this.addxtype({
12571             xns: Roo.bootstrap,
12572
12573             xtype : 'Button',
12574             container_method : 'getButtonContainer' ,            
12575             html :  this.html, // fix changable?
12576             cls : 'w-100 ',
12577             listeners : {
12578                 'click' : function(btn, e) {
12579                     t.onClick(e);
12580                 }
12581             }
12582         });
12583         
12584         
12585         
12586         
12587         this.urlAPI = (window.createObjectURL && window) || 
12588                                 (window.URL && URL.revokeObjectURL && URL) || 
12589                                 (window.webkitURL && webkitURL);
12590                         
12591          
12592          
12593          
12594         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12595         
12596         this.selectorEl.on('change', this.onFileSelected, this);
12597         if (this.images) {
12598             var t = this;
12599             this.images.forEach(function(img) {
12600                 t.addCard(img)
12601             });
12602             this.images = false;
12603         }
12604         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12605          
12606        
12607     },
12608     
12609    
12610     onClick : function(e)
12611     {
12612         e.preventDefault();
12613          
12614         this.selectorEl.dom.click();
12615          
12616     },
12617     
12618     onFileSelected : function(e)
12619     {
12620         e.preventDefault();
12621         
12622         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12623             return;
12624         }
12625         
12626         Roo.each(this.selectorEl.dom.files, function(file){    
12627             this.addFile(file);
12628         }, this);
12629          
12630     },
12631     
12632       
12633     
12634       
12635     
12636     addFile : function(file)
12637     {
12638            
12639         if(typeof(file) === 'string'){
12640             throw "Add file by name?"; // should not happen
12641             return;
12642         }
12643         
12644         if(!file || !this.urlAPI){
12645             return;
12646         }
12647         
12648         // file;
12649         // file.type;
12650         
12651         var _this = this;
12652         
12653         
12654         var url = _this.urlAPI.createObjectURL( file);
12655            
12656         this.addCard({
12657             id : Roo.bootstrap.CardUploader.ID--,
12658             is_uploaded : false,
12659             src : url,
12660             title : file.name,
12661             mimetype : file.type,
12662             preview : false,
12663             is_deleted : 0
12664         })
12665         
12666     },
12667     
12668     addCard : function (data)
12669     {
12670         // hidden input element?
12671         // if the file is not an image...
12672         //then we need to use something other that and header_image
12673         var t = this;
12674         //   remove.....
12675         var footer = [
12676             {
12677                 xns : Roo.bootstrap,
12678                 xtype : 'CardFooter',
12679                 items: [
12680                     {
12681                         xns : Roo.bootstrap,
12682                         xtype : 'Element',
12683                         cls : 'd-flex',
12684                         items : [
12685                             
12686                             {
12687                                 xns : Roo.bootstrap,
12688                                 xtype : 'Button',
12689                                 html : String.format("<small>{0}</small>", data.title),
12690                                 cls : 'col-11 text-left',
12691                                 size: 'sm',
12692                                 weight: 'link',
12693                                 fa : 'download',
12694                                 listeners : {
12695                                     click : function() {
12696                                         this.downloadCard(data.id)
12697                                     }
12698                                 }
12699                             },
12700                           
12701                             {
12702                                 xns : Roo.bootstrap,
12703                                 xtype : 'Button',
12704                                 
12705                                 size : 'sm',
12706                                 weight: 'danger',
12707                                 cls : 'col-1',
12708                                 fa : 'times',
12709                                 listeners : {
12710                                     click : function() {
12711                                         t.removeCard(data.id)
12712                                     }
12713                                 }
12714                             }
12715                         ]
12716                     }
12717                     
12718                 ] 
12719             }
12720             
12721         ];
12722
12723         var cn = this.addxtype(
12724             {
12725                  
12726                 xns : Roo.bootstrap,
12727                 xtype : 'Card',
12728                 closeable : true,
12729                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12730                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12731                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12732                 data : data,
12733                 html : false,
12734                  
12735                 items : footer,
12736                 initEvents : function() {
12737                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12738                     this.imgEl = this.el.select('.card-img-top').first();
12739                     if (this.imgEl) {
12740                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12741                         this.imgEl.set({ 'pointer' : 'cursor' });
12742                                   
12743                     }
12744                     
12745                   
12746                 }
12747                 
12748             }
12749         );
12750         // dont' really need ot update items.
12751         // this.items.push(cn);
12752         this.fileCollection.add(cn);
12753         this.updateInput();
12754         
12755     },
12756     removeCard : function(id)
12757     {
12758         
12759         var card  = this.fileCollection.get(id);
12760         card.data.is_deleted = 1;
12761         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12762         this.fileCollection.remove(card);
12763         //this.items = this.items.filter(function(e) { return e != card });
12764         // dont' really need ot update items.
12765         card.el.dom.parentNode.removeChild(card.el.dom);
12766         
12767     },
12768     reset: function()
12769     {
12770         this.fileCollection.each(function(card) {
12771             card.el.dom.parentNode.removeChild(card.el.dom);    
12772         });
12773         this.fileCollection.clear();
12774         this.updateInput();
12775     },
12776     
12777     updateInput : function()
12778     {
12779         var data = [];
12780         this.fileCollection.each(function(e) {
12781             data.push(e.data);
12782         });
12783         
12784         this.inputEl().dom.value = JSON.stringify(data);
12785     }
12786     
12787     
12788 });
12789
12790
12791 Roo.bootstrap.CardUploader.ID = -1;/*
12792  * Based on:
12793  * Ext JS Library 1.1.1
12794  * Copyright(c) 2006-2007, Ext JS, LLC.
12795  *
12796  * Originally Released Under LGPL - original licence link has changed is not relivant.
12797  *
12798  * Fork - LGPL
12799  * <script type="text/javascript">
12800  */
12801
12802
12803 /**
12804  * @class Roo.data.SortTypes
12805  * @singleton
12806  * Defines the default sorting (casting?) comparison functions used when sorting data.
12807  */
12808 Roo.data.SortTypes = {
12809     /**
12810      * Default sort that does nothing
12811      * @param {Mixed} s The value being converted
12812      * @return {Mixed} The comparison value
12813      */
12814     none : function(s){
12815         return s;
12816     },
12817     
12818     /**
12819      * The regular expression used to strip tags
12820      * @type {RegExp}
12821      * @property
12822      */
12823     stripTagsRE : /<\/?[^>]+>/gi,
12824     
12825     /**
12826      * Strips all HTML tags to sort on text only
12827      * @param {Mixed} s The value being converted
12828      * @return {String} The comparison value
12829      */
12830     asText : function(s){
12831         return String(s).replace(this.stripTagsRE, "");
12832     },
12833     
12834     /**
12835      * Strips all HTML tags to sort on text only - Case insensitive
12836      * @param {Mixed} s The value being converted
12837      * @return {String} The comparison value
12838      */
12839     asUCText : function(s){
12840         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12841     },
12842     
12843     /**
12844      * Case insensitive string
12845      * @param {Mixed} s The value being converted
12846      * @return {String} The comparison value
12847      */
12848     asUCString : function(s) {
12849         return String(s).toUpperCase();
12850     },
12851     
12852     /**
12853      * Date sorting
12854      * @param {Mixed} s The value being converted
12855      * @return {Number} The comparison value
12856      */
12857     asDate : function(s) {
12858         if(!s){
12859             return 0;
12860         }
12861         if(s instanceof Date){
12862             return s.getTime();
12863         }
12864         return Date.parse(String(s));
12865     },
12866     
12867     /**
12868      * Float sorting
12869      * @param {Mixed} s The value being converted
12870      * @return {Float} The comparison value
12871      */
12872     asFloat : function(s) {
12873         var val = parseFloat(String(s).replace(/,/g, ""));
12874         if(isNaN(val)) {
12875             val = 0;
12876         }
12877         return val;
12878     },
12879     
12880     /**
12881      * Integer sorting
12882      * @param {Mixed} s The value being converted
12883      * @return {Number} The comparison value
12884      */
12885     asInt : function(s) {
12886         var val = parseInt(String(s).replace(/,/g, ""));
12887         if(isNaN(val)) {
12888             val = 0;
12889         }
12890         return val;
12891     }
12892 };/*
12893  * Based on:
12894  * Ext JS Library 1.1.1
12895  * Copyright(c) 2006-2007, Ext JS, LLC.
12896  *
12897  * Originally Released Under LGPL - original licence link has changed is not relivant.
12898  *
12899  * Fork - LGPL
12900  * <script type="text/javascript">
12901  */
12902
12903 /**
12904 * @class Roo.data.Record
12905  * Instances of this class encapsulate both record <em>definition</em> information, and record
12906  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12907  * to access Records cached in an {@link Roo.data.Store} object.<br>
12908  * <p>
12909  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12910  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12911  * objects.<br>
12912  * <p>
12913  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12914  * @constructor
12915  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12916  * {@link #create}. The parameters are the same.
12917  * @param {Array} data An associative Array of data values keyed by the field name.
12918  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12919  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12920  * not specified an integer id is generated.
12921  */
12922 Roo.data.Record = function(data, id){
12923     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12924     this.data = data;
12925 };
12926
12927 /**
12928  * Generate a constructor for a specific record layout.
12929  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12930  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12931  * Each field definition object may contain the following properties: <ul>
12932  * <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,
12933  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12934  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12935  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12936  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12937  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12938  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12939  * this may be omitted.</p></li>
12940  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12941  * <ul><li>auto (Default, implies no conversion)</li>
12942  * <li>string</li>
12943  * <li>int</li>
12944  * <li>float</li>
12945  * <li>boolean</li>
12946  * <li>date</li></ul></p></li>
12947  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12948  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12949  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12950  * by the Reader into an object that will be stored in the Record. It is passed the
12951  * following parameters:<ul>
12952  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12953  * </ul></p></li>
12954  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12955  * </ul>
12956  * <br>usage:<br><pre><code>
12957 var TopicRecord = Roo.data.Record.create(
12958     {name: 'title', mapping: 'topic_title'},
12959     {name: 'author', mapping: 'username'},
12960     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12961     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12962     {name: 'lastPoster', mapping: 'user2'},
12963     {name: 'excerpt', mapping: 'post_text'}
12964 );
12965
12966 var myNewRecord = new TopicRecord({
12967     title: 'Do my job please',
12968     author: 'noobie',
12969     totalPosts: 1,
12970     lastPost: new Date(),
12971     lastPoster: 'Animal',
12972     excerpt: 'No way dude!'
12973 });
12974 myStore.add(myNewRecord);
12975 </code></pre>
12976  * @method create
12977  * @static
12978  */
12979 Roo.data.Record.create = function(o){
12980     var f = function(){
12981         f.superclass.constructor.apply(this, arguments);
12982     };
12983     Roo.extend(f, Roo.data.Record);
12984     var p = f.prototype;
12985     p.fields = new Roo.util.MixedCollection(false, function(field){
12986         return field.name;
12987     });
12988     for(var i = 0, len = o.length; i < len; i++){
12989         p.fields.add(new Roo.data.Field(o[i]));
12990     }
12991     f.getField = function(name){
12992         return p.fields.get(name);  
12993     };
12994     return f;
12995 };
12996
12997 Roo.data.Record.AUTO_ID = 1000;
12998 Roo.data.Record.EDIT = 'edit';
12999 Roo.data.Record.REJECT = 'reject';
13000 Roo.data.Record.COMMIT = 'commit';
13001
13002 Roo.data.Record.prototype = {
13003     /**
13004      * Readonly flag - true if this record has been modified.
13005      * @type Boolean
13006      */
13007     dirty : false,
13008     editing : false,
13009     error: null,
13010     modified: null,
13011
13012     // private
13013     join : function(store){
13014         this.store = store;
13015     },
13016
13017     /**
13018      * Set the named field to the specified value.
13019      * @param {String} name The name of the field to set.
13020      * @param {Object} value The value to set the field to.
13021      */
13022     set : function(name, value){
13023         if(this.data[name] == value){
13024             return;
13025         }
13026         this.dirty = true;
13027         if(!this.modified){
13028             this.modified = {};
13029         }
13030         if(typeof this.modified[name] == 'undefined'){
13031             this.modified[name] = this.data[name];
13032         }
13033         this.data[name] = value;
13034         if(!this.editing && this.store){
13035             this.store.afterEdit(this);
13036         }       
13037     },
13038
13039     /**
13040      * Get the value of the named field.
13041      * @param {String} name The name of the field to get the value of.
13042      * @return {Object} The value of the field.
13043      */
13044     get : function(name){
13045         return this.data[name]; 
13046     },
13047
13048     // private
13049     beginEdit : function(){
13050         this.editing = true;
13051         this.modified = {}; 
13052     },
13053
13054     // private
13055     cancelEdit : function(){
13056         this.editing = false;
13057         delete this.modified;
13058     },
13059
13060     // private
13061     endEdit : function(){
13062         this.editing = false;
13063         if(this.dirty && this.store){
13064             this.store.afterEdit(this);
13065         }
13066     },
13067
13068     /**
13069      * Usually called by the {@link Roo.data.Store} which owns the Record.
13070      * Rejects all changes made to the Record since either creation, or the last commit operation.
13071      * Modified fields are reverted to their original values.
13072      * <p>
13073      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13074      * of reject operations.
13075      */
13076     reject : function(){
13077         var m = this.modified;
13078         for(var n in m){
13079             if(typeof m[n] != "function"){
13080                 this.data[n] = m[n];
13081             }
13082         }
13083         this.dirty = false;
13084         delete this.modified;
13085         this.editing = false;
13086         if(this.store){
13087             this.store.afterReject(this);
13088         }
13089     },
13090
13091     /**
13092      * Usually called by the {@link Roo.data.Store} which owns the Record.
13093      * Commits all changes made to the Record since either creation, or the last commit operation.
13094      * <p>
13095      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13096      * of commit operations.
13097      */
13098     commit : function(){
13099         this.dirty = false;
13100         delete this.modified;
13101         this.editing = false;
13102         if(this.store){
13103             this.store.afterCommit(this);
13104         }
13105     },
13106
13107     // private
13108     hasError : function(){
13109         return this.error != null;
13110     },
13111
13112     // private
13113     clearError : function(){
13114         this.error = null;
13115     },
13116
13117     /**
13118      * Creates a copy of this record.
13119      * @param {String} id (optional) A new record id if you don't want to use this record's id
13120      * @return {Record}
13121      */
13122     copy : function(newId) {
13123         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13124     }
13125 };/*
13126  * Based on:
13127  * Ext JS Library 1.1.1
13128  * Copyright(c) 2006-2007, Ext JS, LLC.
13129  *
13130  * Originally Released Under LGPL - original licence link has changed is not relivant.
13131  *
13132  * Fork - LGPL
13133  * <script type="text/javascript">
13134  */
13135
13136
13137
13138 /**
13139  * @class Roo.data.Store
13140  * @extends Roo.util.Observable
13141  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13142  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13143  * <p>
13144  * 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
13145  * has no knowledge of the format of the data returned by the Proxy.<br>
13146  * <p>
13147  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13148  * instances from the data object. These records are cached and made available through accessor functions.
13149  * @constructor
13150  * Creates a new Store.
13151  * @param {Object} config A config object containing the objects needed for the Store to access data,
13152  * and read the data into Records.
13153  */
13154 Roo.data.Store = function(config){
13155     this.data = new Roo.util.MixedCollection(false);
13156     this.data.getKey = function(o){
13157         return o.id;
13158     };
13159     this.baseParams = {};
13160     // private
13161     this.paramNames = {
13162         "start" : "start",
13163         "limit" : "limit",
13164         "sort" : "sort",
13165         "dir" : "dir",
13166         "multisort" : "_multisort"
13167     };
13168
13169     if(config && config.data){
13170         this.inlineData = config.data;
13171         delete config.data;
13172     }
13173
13174     Roo.apply(this, config);
13175     
13176     if(this.reader){ // reader passed
13177         this.reader = Roo.factory(this.reader, Roo.data);
13178         this.reader.xmodule = this.xmodule || false;
13179         if(!this.recordType){
13180             this.recordType = this.reader.recordType;
13181         }
13182         if(this.reader.onMetaChange){
13183             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13184         }
13185     }
13186
13187     if(this.recordType){
13188         this.fields = this.recordType.prototype.fields;
13189     }
13190     this.modified = [];
13191
13192     this.addEvents({
13193         /**
13194          * @event datachanged
13195          * Fires when the data cache has changed, and a widget which is using this Store
13196          * as a Record cache should refresh its view.
13197          * @param {Store} this
13198          */
13199         datachanged : true,
13200         /**
13201          * @event metachange
13202          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13203          * @param {Store} this
13204          * @param {Object} meta The JSON metadata
13205          */
13206         metachange : true,
13207         /**
13208          * @event add
13209          * Fires when Records have been added to the Store
13210          * @param {Store} this
13211          * @param {Roo.data.Record[]} records The array of Records added
13212          * @param {Number} index The index at which the record(s) were added
13213          */
13214         add : true,
13215         /**
13216          * @event remove
13217          * Fires when a Record has been removed from the Store
13218          * @param {Store} this
13219          * @param {Roo.data.Record} record The Record that was removed
13220          * @param {Number} index The index at which the record was removed
13221          */
13222         remove : true,
13223         /**
13224          * @event update
13225          * Fires when a Record has been updated
13226          * @param {Store} this
13227          * @param {Roo.data.Record} record The Record that was updated
13228          * @param {String} operation The update operation being performed.  Value may be one of:
13229          * <pre><code>
13230  Roo.data.Record.EDIT
13231  Roo.data.Record.REJECT
13232  Roo.data.Record.COMMIT
13233          * </code></pre>
13234          */
13235         update : true,
13236         /**
13237          * @event clear
13238          * Fires when the data cache has been cleared.
13239          * @param {Store} this
13240          */
13241         clear : true,
13242         /**
13243          * @event beforeload
13244          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13245          * the load action will be canceled.
13246          * @param {Store} this
13247          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13248          */
13249         beforeload : true,
13250         /**
13251          * @event beforeloadadd
13252          * Fires after a new set of Records has been loaded.
13253          * @param {Store} this
13254          * @param {Roo.data.Record[]} records The Records that were loaded
13255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13256          */
13257         beforeloadadd : true,
13258         /**
13259          * @event load
13260          * Fires after a new set of Records has been loaded, before they are added to the store.
13261          * @param {Store} this
13262          * @param {Roo.data.Record[]} records The Records that were loaded
13263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13264          * @params {Object} return from reader
13265          */
13266         load : true,
13267         /**
13268          * @event loadexception
13269          * Fires if an exception occurs in the Proxy during loading.
13270          * Called with the signature of the Proxy's "loadexception" event.
13271          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13272          * 
13273          * @param {Proxy} 
13274          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13275          * @param {Object} load options 
13276          * @param {Object} jsonData from your request (normally this contains the Exception)
13277          */
13278         loadexception : true
13279     });
13280     
13281     if(this.proxy){
13282         this.proxy = Roo.factory(this.proxy, Roo.data);
13283         this.proxy.xmodule = this.xmodule || false;
13284         this.relayEvents(this.proxy,  ["loadexception"]);
13285     }
13286     this.sortToggle = {};
13287     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13288
13289     Roo.data.Store.superclass.constructor.call(this);
13290
13291     if(this.inlineData){
13292         this.loadData(this.inlineData);
13293         delete this.inlineData;
13294     }
13295 };
13296
13297 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13298      /**
13299     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13300     * without a remote query - used by combo/forms at present.
13301     */
13302     
13303     /**
13304     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13305     */
13306     /**
13307     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13308     */
13309     /**
13310     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13311     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13312     */
13313     /**
13314     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13315     * on any HTTP request
13316     */
13317     /**
13318     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13319     */
13320     /**
13321     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13322     */
13323     multiSort: false,
13324     /**
13325     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13326     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13327     */
13328     remoteSort : false,
13329
13330     /**
13331     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13332      * loaded or when a record is removed. (defaults to false).
13333     */
13334     pruneModifiedRecords : false,
13335
13336     // private
13337     lastOptions : null,
13338
13339     /**
13340      * Add Records to the Store and fires the add event.
13341      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13342      */
13343     add : function(records){
13344         records = [].concat(records);
13345         for(var i = 0, len = records.length; i < len; i++){
13346             records[i].join(this);
13347         }
13348         var index = this.data.length;
13349         this.data.addAll(records);
13350         this.fireEvent("add", this, records, index);
13351     },
13352
13353     /**
13354      * Remove a Record from the Store and fires the remove event.
13355      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13356      */
13357     remove : function(record){
13358         var index = this.data.indexOf(record);
13359         this.data.removeAt(index);
13360  
13361         if(this.pruneModifiedRecords){
13362             this.modified.remove(record);
13363         }
13364         this.fireEvent("remove", this, record, index);
13365     },
13366
13367     /**
13368      * Remove all Records from the Store and fires the clear event.
13369      */
13370     removeAll : function(){
13371         this.data.clear();
13372         if(this.pruneModifiedRecords){
13373             this.modified = [];
13374         }
13375         this.fireEvent("clear", this);
13376     },
13377
13378     /**
13379      * Inserts Records to the Store at the given index and fires the add event.
13380      * @param {Number} index The start index at which to insert the passed Records.
13381      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13382      */
13383     insert : function(index, records){
13384         records = [].concat(records);
13385         for(var i = 0, len = records.length; i < len; i++){
13386             this.data.insert(index, records[i]);
13387             records[i].join(this);
13388         }
13389         this.fireEvent("add", this, records, index);
13390     },
13391
13392     /**
13393      * Get the index within the cache of the passed Record.
13394      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13395      * @return {Number} The index of the passed Record. Returns -1 if not found.
13396      */
13397     indexOf : function(record){
13398         return this.data.indexOf(record);
13399     },
13400
13401     /**
13402      * Get the index within the cache of the Record with the passed id.
13403      * @param {String} id The id of the Record to find.
13404      * @return {Number} The index of the Record. Returns -1 if not found.
13405      */
13406     indexOfId : function(id){
13407         return this.data.indexOfKey(id);
13408     },
13409
13410     /**
13411      * Get the Record with the specified id.
13412      * @param {String} id The id of the Record to find.
13413      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13414      */
13415     getById : function(id){
13416         return this.data.key(id);
13417     },
13418
13419     /**
13420      * Get the Record at the specified index.
13421      * @param {Number} index The index of the Record to find.
13422      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13423      */
13424     getAt : function(index){
13425         return this.data.itemAt(index);
13426     },
13427
13428     /**
13429      * Returns a range of Records between specified indices.
13430      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13431      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13432      * @return {Roo.data.Record[]} An array of Records
13433      */
13434     getRange : function(start, end){
13435         return this.data.getRange(start, end);
13436     },
13437
13438     // private
13439     storeOptions : function(o){
13440         o = Roo.apply({}, o);
13441         delete o.callback;
13442         delete o.scope;
13443         this.lastOptions = o;
13444     },
13445
13446     /**
13447      * Loads the Record cache from the configured Proxy using the configured Reader.
13448      * <p>
13449      * If using remote paging, then the first load call must specify the <em>start</em>
13450      * and <em>limit</em> properties in the options.params property to establish the initial
13451      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13452      * <p>
13453      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13454      * and this call will return before the new data has been loaded. Perform any post-processing
13455      * in a callback function, or in a "load" event handler.</strong>
13456      * <p>
13457      * @param {Object} options An object containing properties which control loading options:<ul>
13458      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13459      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13460      * passed the following arguments:<ul>
13461      * <li>r : Roo.data.Record[]</li>
13462      * <li>options: Options object from the load call</li>
13463      * <li>success: Boolean success indicator</li></ul></li>
13464      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13465      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13466      * </ul>
13467      */
13468     load : function(options){
13469         options = options || {};
13470         if(this.fireEvent("beforeload", this, options) !== false){
13471             this.storeOptions(options);
13472             var p = Roo.apply(options.params || {}, this.baseParams);
13473             // if meta was not loaded from remote source.. try requesting it.
13474             if (!this.reader.metaFromRemote) {
13475                 p._requestMeta = 1;
13476             }
13477             if(this.sortInfo && this.remoteSort){
13478                 var pn = this.paramNames;
13479                 p[pn["sort"]] = this.sortInfo.field;
13480                 p[pn["dir"]] = this.sortInfo.direction;
13481             }
13482             if (this.multiSort) {
13483                 var pn = this.paramNames;
13484                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13485             }
13486             
13487             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13488         }
13489     },
13490
13491     /**
13492      * Reloads the Record cache from the configured Proxy using the configured Reader and
13493      * the options from the last load operation performed.
13494      * @param {Object} options (optional) An object containing properties which may override the options
13495      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13496      * the most recently used options are reused).
13497      */
13498     reload : function(options){
13499         this.load(Roo.applyIf(options||{}, this.lastOptions));
13500     },
13501
13502     // private
13503     // Called as a callback by the Reader during a load operation.
13504     loadRecords : function(o, options, success){
13505         if(!o || success === false){
13506             if(success !== false){
13507                 this.fireEvent("load", this, [], options, o);
13508             }
13509             if(options.callback){
13510                 options.callback.call(options.scope || this, [], options, false);
13511             }
13512             return;
13513         }
13514         // if data returned failure - throw an exception.
13515         if (o.success === false) {
13516             // show a message if no listener is registered.
13517             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13518                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13519             }
13520             // loadmask wil be hooked into this..
13521             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13522             return;
13523         }
13524         var r = o.records, t = o.totalRecords || r.length;
13525         
13526         this.fireEvent("beforeloadadd", this, r, options, o);
13527         
13528         if(!options || options.add !== true){
13529             if(this.pruneModifiedRecords){
13530                 this.modified = [];
13531             }
13532             for(var i = 0, len = r.length; i < len; i++){
13533                 r[i].join(this);
13534             }
13535             if(this.snapshot){
13536                 this.data = this.snapshot;
13537                 delete this.snapshot;
13538             }
13539             this.data.clear();
13540             this.data.addAll(r);
13541             this.totalLength = t;
13542             this.applySort();
13543             this.fireEvent("datachanged", this);
13544         }else{
13545             this.totalLength = Math.max(t, this.data.length+r.length);
13546             this.add(r);
13547         }
13548         
13549         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13550                 
13551             var e = new Roo.data.Record({});
13552
13553             e.set(this.parent.displayField, this.parent.emptyTitle);
13554             e.set(this.parent.valueField, '');
13555
13556             this.insert(0, e);
13557         }
13558             
13559         this.fireEvent("load", this, r, options, o);
13560         if(options.callback){
13561             options.callback.call(options.scope || this, r, options, true);
13562         }
13563     },
13564
13565
13566     /**
13567      * Loads data from a passed data block. A Reader which understands the format of the data
13568      * must have been configured in the constructor.
13569      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13570      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13571      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13572      */
13573     loadData : function(o, append){
13574         var r = this.reader.readRecords(o);
13575         this.loadRecords(r, {add: append}, true);
13576     },
13577     
13578      /**
13579      * using 'cn' the nested child reader read the child array into it's child stores.
13580      * @param {Object} rec The record with a 'children array
13581      */
13582     loadDataFromChildren : function(rec)
13583     {
13584         this.loadData(this.reader.toLoadData(rec));
13585     },
13586     
13587
13588     /**
13589      * Gets the number of cached records.
13590      * <p>
13591      * <em>If using paging, this may not be the total size of the dataset. If the data object
13592      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13593      * the data set size</em>
13594      */
13595     getCount : function(){
13596         return this.data.length || 0;
13597     },
13598
13599     /**
13600      * Gets the total number of records in the dataset as returned by the server.
13601      * <p>
13602      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13603      * the dataset size</em>
13604      */
13605     getTotalCount : function(){
13606         return this.totalLength || 0;
13607     },
13608
13609     /**
13610      * Returns the sort state of the Store as an object with two properties:
13611      * <pre><code>
13612  field {String} The name of the field by which the Records are sorted
13613  direction {String} The sort order, "ASC" or "DESC"
13614      * </code></pre>
13615      */
13616     getSortState : function(){
13617         return this.sortInfo;
13618     },
13619
13620     // private
13621     applySort : function(){
13622         if(this.sortInfo && !this.remoteSort){
13623             var s = this.sortInfo, f = s.field;
13624             var st = this.fields.get(f).sortType;
13625             var fn = function(r1, r2){
13626                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13627                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13628             };
13629             this.data.sort(s.direction, fn);
13630             if(this.snapshot && this.snapshot != this.data){
13631                 this.snapshot.sort(s.direction, fn);
13632             }
13633         }
13634     },
13635
13636     /**
13637      * Sets the default sort column and order to be used by the next load operation.
13638      * @param {String} fieldName The name of the field to sort by.
13639      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13640      */
13641     setDefaultSort : function(field, dir){
13642         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13643     },
13644
13645     /**
13646      * Sort the Records.
13647      * If remote sorting is used, the sort is performed on the server, and the cache is
13648      * reloaded. If local sorting is used, the cache is sorted internally.
13649      * @param {String} fieldName The name of the field to sort by.
13650      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13651      */
13652     sort : function(fieldName, dir){
13653         var f = this.fields.get(fieldName);
13654         if(!dir){
13655             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13656             
13657             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13658                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13659             }else{
13660                 dir = f.sortDir;
13661             }
13662         }
13663         this.sortToggle[f.name] = dir;
13664         this.sortInfo = {field: f.name, direction: dir};
13665         if(!this.remoteSort){
13666             this.applySort();
13667             this.fireEvent("datachanged", this);
13668         }else{
13669             this.load(this.lastOptions);
13670         }
13671     },
13672
13673     /**
13674      * Calls the specified function for each of the Records in the cache.
13675      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13676      * Returning <em>false</em> aborts and exits the iteration.
13677      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13678      */
13679     each : function(fn, scope){
13680         this.data.each(fn, scope);
13681     },
13682
13683     /**
13684      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13685      * (e.g., during paging).
13686      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13687      */
13688     getModifiedRecords : function(){
13689         return this.modified;
13690     },
13691
13692     // private
13693     createFilterFn : function(property, value, anyMatch){
13694         if(!value.exec){ // not a regex
13695             value = String(value);
13696             if(value.length == 0){
13697                 return false;
13698             }
13699             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13700         }
13701         return function(r){
13702             return value.test(r.data[property]);
13703         };
13704     },
13705
13706     /**
13707      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13708      * @param {String} property A field on your records
13709      * @param {Number} start The record index to start at (defaults to 0)
13710      * @param {Number} end The last record index to include (defaults to length - 1)
13711      * @return {Number} The sum
13712      */
13713     sum : function(property, start, end){
13714         var rs = this.data.items, v = 0;
13715         start = start || 0;
13716         end = (end || end === 0) ? end : rs.length-1;
13717
13718         for(var i = start; i <= end; i++){
13719             v += (rs[i].data[property] || 0);
13720         }
13721         return v;
13722     },
13723
13724     /**
13725      * Filter the records by a specified property.
13726      * @param {String} field A field on your records
13727      * @param {String/RegExp} value Either a string that the field
13728      * should start with or a RegExp to test against the field
13729      * @param {Boolean} anyMatch True to match any part not just the beginning
13730      */
13731     filter : function(property, value, anyMatch){
13732         var fn = this.createFilterFn(property, value, anyMatch);
13733         return fn ? this.filterBy(fn) : this.clearFilter();
13734     },
13735
13736     /**
13737      * Filter by a function. The specified function will be called with each
13738      * record in this data source. If the function returns true the record is included,
13739      * otherwise it is filtered.
13740      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13741      * @param {Object} scope (optional) The scope of the function (defaults to this)
13742      */
13743     filterBy : function(fn, scope){
13744         this.snapshot = this.snapshot || this.data;
13745         this.data = this.queryBy(fn, scope||this);
13746         this.fireEvent("datachanged", this);
13747     },
13748
13749     /**
13750      * Query the records by a specified property.
13751      * @param {String} field A field on your records
13752      * @param {String/RegExp} value Either a string that the field
13753      * should start with or a RegExp to test against the field
13754      * @param {Boolean} anyMatch True to match any part not just the beginning
13755      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13756      */
13757     query : function(property, value, anyMatch){
13758         var fn = this.createFilterFn(property, value, anyMatch);
13759         return fn ? this.queryBy(fn) : this.data.clone();
13760     },
13761
13762     /**
13763      * Query by a function. The specified function will be called with each
13764      * record in this data source. If the function returns true the record is included
13765      * in the results.
13766      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13767      * @param {Object} scope (optional) The scope of the function (defaults to this)
13768       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13769      **/
13770     queryBy : function(fn, scope){
13771         var data = this.snapshot || this.data;
13772         return data.filterBy(fn, scope||this);
13773     },
13774
13775     /**
13776      * Collects unique values for a particular dataIndex from this store.
13777      * @param {String} dataIndex The property to collect
13778      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13779      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13780      * @return {Array} An array of the unique values
13781      **/
13782     collect : function(dataIndex, allowNull, bypassFilter){
13783         var d = (bypassFilter === true && this.snapshot) ?
13784                 this.snapshot.items : this.data.items;
13785         var v, sv, r = [], l = {};
13786         for(var i = 0, len = d.length; i < len; i++){
13787             v = d[i].data[dataIndex];
13788             sv = String(v);
13789             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13790                 l[sv] = true;
13791                 r[r.length] = v;
13792             }
13793         }
13794         return r;
13795     },
13796
13797     /**
13798      * Revert to a view of the Record cache with no filtering applied.
13799      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13800      */
13801     clearFilter : function(suppressEvent){
13802         if(this.snapshot && this.snapshot != this.data){
13803             this.data = this.snapshot;
13804             delete this.snapshot;
13805             if(suppressEvent !== true){
13806                 this.fireEvent("datachanged", this);
13807             }
13808         }
13809     },
13810
13811     // private
13812     afterEdit : function(record){
13813         if(this.modified.indexOf(record) == -1){
13814             this.modified.push(record);
13815         }
13816         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13817     },
13818     
13819     // private
13820     afterReject : function(record){
13821         this.modified.remove(record);
13822         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13823     },
13824
13825     // private
13826     afterCommit : function(record){
13827         this.modified.remove(record);
13828         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13829     },
13830
13831     /**
13832      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13833      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13834      */
13835     commitChanges : function(){
13836         var m = this.modified.slice(0);
13837         this.modified = [];
13838         for(var i = 0, len = m.length; i < len; i++){
13839             m[i].commit();
13840         }
13841     },
13842
13843     /**
13844      * Cancel outstanding changes on all changed records.
13845      */
13846     rejectChanges : function(){
13847         var m = this.modified.slice(0);
13848         this.modified = [];
13849         for(var i = 0, len = m.length; i < len; i++){
13850             m[i].reject();
13851         }
13852     },
13853
13854     onMetaChange : function(meta, rtype, o){
13855         this.recordType = rtype;
13856         this.fields = rtype.prototype.fields;
13857         delete this.snapshot;
13858         this.sortInfo = meta.sortInfo || this.sortInfo;
13859         this.modified = [];
13860         this.fireEvent('metachange', this, this.reader.meta);
13861     },
13862     
13863     moveIndex : function(data, type)
13864     {
13865         var index = this.indexOf(data);
13866         
13867         var newIndex = index + type;
13868         
13869         this.remove(data);
13870         
13871         this.insert(newIndex, data);
13872         
13873     }
13874 });/*
13875  * Based on:
13876  * Ext JS Library 1.1.1
13877  * Copyright(c) 2006-2007, Ext JS, LLC.
13878  *
13879  * Originally Released Under LGPL - original licence link has changed is not relivant.
13880  *
13881  * Fork - LGPL
13882  * <script type="text/javascript">
13883  */
13884
13885 /**
13886  * @class Roo.data.SimpleStore
13887  * @extends Roo.data.Store
13888  * Small helper class to make creating Stores from Array data easier.
13889  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13890  * @cfg {Array} fields An array of field definition objects, or field name strings.
13891  * @cfg {Object} an existing reader (eg. copied from another store)
13892  * @cfg {Array} data The multi-dimensional array of data
13893  * @constructor
13894  * @param {Object} config
13895  */
13896 Roo.data.SimpleStore = function(config)
13897 {
13898     Roo.data.SimpleStore.superclass.constructor.call(this, {
13899         isLocal : true,
13900         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13901                 id: config.id
13902             },
13903             Roo.data.Record.create(config.fields)
13904         ),
13905         proxy : new Roo.data.MemoryProxy(config.data)
13906     });
13907     this.load();
13908 };
13909 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13910  * Based on:
13911  * Ext JS Library 1.1.1
13912  * Copyright(c) 2006-2007, Ext JS, LLC.
13913  *
13914  * Originally Released Under LGPL - original licence link has changed is not relivant.
13915  *
13916  * Fork - LGPL
13917  * <script type="text/javascript">
13918  */
13919
13920 /**
13921 /**
13922  * @extends Roo.data.Store
13923  * @class Roo.data.JsonStore
13924  * Small helper class to make creating Stores for JSON data easier. <br/>
13925 <pre><code>
13926 var store = new Roo.data.JsonStore({
13927     url: 'get-images.php',
13928     root: 'images',
13929     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13930 });
13931 </code></pre>
13932  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13933  * JsonReader and HttpProxy (unless inline data is provided).</b>
13934  * @cfg {Array} fields An array of field definition objects, or field name strings.
13935  * @constructor
13936  * @param {Object} config
13937  */
13938 Roo.data.JsonStore = function(c){
13939     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13940         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13941         reader: new Roo.data.JsonReader(c, c.fields)
13942     }));
13943 };
13944 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13945  * Based on:
13946  * Ext JS Library 1.1.1
13947  * Copyright(c) 2006-2007, Ext JS, LLC.
13948  *
13949  * Originally Released Under LGPL - original licence link has changed is not relivant.
13950  *
13951  * Fork - LGPL
13952  * <script type="text/javascript">
13953  */
13954
13955  
13956 Roo.data.Field = function(config){
13957     if(typeof config == "string"){
13958         config = {name: config};
13959     }
13960     Roo.apply(this, config);
13961     
13962     if(!this.type){
13963         this.type = "auto";
13964     }
13965     
13966     var st = Roo.data.SortTypes;
13967     // named sortTypes are supported, here we look them up
13968     if(typeof this.sortType == "string"){
13969         this.sortType = st[this.sortType];
13970     }
13971     
13972     // set default sortType for strings and dates
13973     if(!this.sortType){
13974         switch(this.type){
13975             case "string":
13976                 this.sortType = st.asUCString;
13977                 break;
13978             case "date":
13979                 this.sortType = st.asDate;
13980                 break;
13981             default:
13982                 this.sortType = st.none;
13983         }
13984     }
13985
13986     // define once
13987     var stripRe = /[\$,%]/g;
13988
13989     // prebuilt conversion function for this field, instead of
13990     // switching every time we're reading a value
13991     if(!this.convert){
13992         var cv, dateFormat = this.dateFormat;
13993         switch(this.type){
13994             case "":
13995             case "auto":
13996             case undefined:
13997                 cv = function(v){ return v; };
13998                 break;
13999             case "string":
14000                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14001                 break;
14002             case "int":
14003                 cv = function(v){
14004                     return v !== undefined && v !== null && v !== '' ?
14005                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14006                     };
14007                 break;
14008             case "float":
14009                 cv = function(v){
14010                     return v !== undefined && v !== null && v !== '' ?
14011                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14012                     };
14013                 break;
14014             case "bool":
14015             case "boolean":
14016                 cv = function(v){ return v === true || v === "true" || v == 1; };
14017                 break;
14018             case "date":
14019                 cv = function(v){
14020                     if(!v){
14021                         return '';
14022                     }
14023                     if(v instanceof Date){
14024                         return v;
14025                     }
14026                     if(dateFormat){
14027                         if(dateFormat == "timestamp"){
14028                             return new Date(v*1000);
14029                         }
14030                         return Date.parseDate(v, dateFormat);
14031                     }
14032                     var parsed = Date.parse(v);
14033                     return parsed ? new Date(parsed) : null;
14034                 };
14035              break;
14036             
14037         }
14038         this.convert = cv;
14039     }
14040 };
14041
14042 Roo.data.Field.prototype = {
14043     dateFormat: null,
14044     defaultValue: "",
14045     mapping: null,
14046     sortType : null,
14047     sortDir : "ASC"
14048 };/*
14049  * Based on:
14050  * Ext JS Library 1.1.1
14051  * Copyright(c) 2006-2007, Ext JS, LLC.
14052  *
14053  * Originally Released Under LGPL - original licence link has changed is not relivant.
14054  *
14055  * Fork - LGPL
14056  * <script type="text/javascript">
14057  */
14058  
14059 // Base class for reading structured data from a data source.  This class is intended to be
14060 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14061
14062 /**
14063  * @class Roo.data.DataReader
14064  * Base class for reading structured data from a data source.  This class is intended to be
14065  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14066  */
14067
14068 Roo.data.DataReader = function(meta, recordType){
14069     
14070     this.meta = meta;
14071     
14072     this.recordType = recordType instanceof Array ? 
14073         Roo.data.Record.create(recordType) : recordType;
14074 };
14075
14076 Roo.data.DataReader.prototype = {
14077     
14078     
14079     readerType : 'Data',
14080      /**
14081      * Create an empty record
14082      * @param {Object} data (optional) - overlay some values
14083      * @return {Roo.data.Record} record created.
14084      */
14085     newRow :  function(d) {
14086         var da =  {};
14087         this.recordType.prototype.fields.each(function(c) {
14088             switch( c.type) {
14089                 case 'int' : da[c.name] = 0; break;
14090                 case 'date' : da[c.name] = new Date(); break;
14091                 case 'float' : da[c.name] = 0.0; break;
14092                 case 'boolean' : da[c.name] = false; break;
14093                 default : da[c.name] = ""; break;
14094             }
14095             
14096         });
14097         return new this.recordType(Roo.apply(da, d));
14098     }
14099     
14100     
14101 };/*
14102  * Based on:
14103  * Ext JS Library 1.1.1
14104  * Copyright(c) 2006-2007, Ext JS, LLC.
14105  *
14106  * Originally Released Under LGPL - original licence link has changed is not relivant.
14107  *
14108  * Fork - LGPL
14109  * <script type="text/javascript">
14110  */
14111
14112 /**
14113  * @class Roo.data.DataProxy
14114  * @extends Roo.data.Observable
14115  * This class is an abstract base class for implementations which provide retrieval of
14116  * unformatted data objects.<br>
14117  * <p>
14118  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14119  * (of the appropriate type which knows how to parse the data object) to provide a block of
14120  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14121  * <p>
14122  * Custom implementations must implement the load method as described in
14123  * {@link Roo.data.HttpProxy#load}.
14124  */
14125 Roo.data.DataProxy = function(){
14126     this.addEvents({
14127         /**
14128          * @event beforeload
14129          * Fires before a network request is made to retrieve a data object.
14130          * @param {Object} This DataProxy object.
14131          * @param {Object} params The params parameter to the load function.
14132          */
14133         beforeload : true,
14134         /**
14135          * @event load
14136          * Fires before the load method's callback is called.
14137          * @param {Object} This DataProxy object.
14138          * @param {Object} o The data object.
14139          * @param {Object} arg The callback argument object passed to the load function.
14140          */
14141         load : true,
14142         /**
14143          * @event loadexception
14144          * Fires if an Exception occurs during data retrieval.
14145          * @param {Object} This DataProxy object.
14146          * @param {Object} o The data object.
14147          * @param {Object} arg The callback argument object passed to the load function.
14148          * @param {Object} e The Exception.
14149          */
14150         loadexception : true
14151     });
14152     Roo.data.DataProxy.superclass.constructor.call(this);
14153 };
14154
14155 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14156
14157     /**
14158      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14159      */
14160 /*
14161  * Based on:
14162  * Ext JS Library 1.1.1
14163  * Copyright(c) 2006-2007, Ext JS, LLC.
14164  *
14165  * Originally Released Under LGPL - original licence link has changed is not relivant.
14166  *
14167  * Fork - LGPL
14168  * <script type="text/javascript">
14169  */
14170 /**
14171  * @class Roo.data.MemoryProxy
14172  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14173  * to the Reader when its load method is called.
14174  * @constructor
14175  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14176  */
14177 Roo.data.MemoryProxy = function(data){
14178     if (data.data) {
14179         data = data.data;
14180     }
14181     Roo.data.MemoryProxy.superclass.constructor.call(this);
14182     this.data = data;
14183 };
14184
14185 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14186     
14187     /**
14188      * Load data from the requested source (in this case an in-memory
14189      * data object passed to the constructor), read the data object into
14190      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14191      * process that block using the passed callback.
14192      * @param {Object} params This parameter is not used by the MemoryProxy class.
14193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14194      * object into a block of Roo.data.Records.
14195      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14196      * The function must be passed <ul>
14197      * <li>The Record block object</li>
14198      * <li>The "arg" argument from the load function</li>
14199      * <li>A boolean success indicator</li>
14200      * </ul>
14201      * @param {Object} scope The scope in which to call the callback
14202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14203      */
14204     load : function(params, reader, callback, scope, arg){
14205         params = params || {};
14206         var result;
14207         try {
14208             result = reader.readRecords(params.data ? params.data :this.data);
14209         }catch(e){
14210             this.fireEvent("loadexception", this, arg, null, e);
14211             callback.call(scope, null, arg, false);
14212             return;
14213         }
14214         callback.call(scope, result, arg, true);
14215     },
14216     
14217     // private
14218     update : function(params, records){
14219         
14220     }
14221 });/*
14222  * Based on:
14223  * Ext JS Library 1.1.1
14224  * Copyright(c) 2006-2007, Ext JS, LLC.
14225  *
14226  * Originally Released Under LGPL - original licence link has changed is not relivant.
14227  *
14228  * Fork - LGPL
14229  * <script type="text/javascript">
14230  */
14231 /**
14232  * @class Roo.data.HttpProxy
14233  * @extends Roo.data.DataProxy
14234  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14235  * configured to reference a certain URL.<br><br>
14236  * <p>
14237  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14238  * from which the running page was served.<br><br>
14239  * <p>
14240  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14241  * <p>
14242  * Be aware that to enable the browser to parse an XML document, the server must set
14243  * the Content-Type header in the HTTP response to "text/xml".
14244  * @constructor
14245  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14246  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14247  * will be used to make the request.
14248  */
14249 Roo.data.HttpProxy = function(conn){
14250     Roo.data.HttpProxy.superclass.constructor.call(this);
14251     // is conn a conn config or a real conn?
14252     this.conn = conn;
14253     this.useAjax = !conn || !conn.events;
14254   
14255 };
14256
14257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14258     // thse are take from connection...
14259     
14260     /**
14261      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14262      */
14263     /**
14264      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14265      * extra parameters to each request made by this object. (defaults to undefined)
14266      */
14267     /**
14268      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14269      *  to each request made by this object. (defaults to undefined)
14270      */
14271     /**
14272      * @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)
14273      */
14274     /**
14275      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14276      */
14277      /**
14278      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14279      * @type Boolean
14280      */
14281   
14282
14283     /**
14284      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14285      * @type Boolean
14286      */
14287     /**
14288      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14289      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14290      * a finer-grained basis than the DataProxy events.
14291      */
14292     getConnection : function(){
14293         return this.useAjax ? Roo.Ajax : this.conn;
14294     },
14295
14296     /**
14297      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14298      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14299      * process that block using the passed callback.
14300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14301      * for the request to the remote server.
14302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14303      * object into a block of Roo.data.Records.
14304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14305      * The function must be passed <ul>
14306      * <li>The Record block object</li>
14307      * <li>The "arg" argument from the load function</li>
14308      * <li>A boolean success indicator</li>
14309      * </ul>
14310      * @param {Object} scope The scope in which to call the callback
14311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14312      */
14313     load : function(params, reader, callback, scope, arg){
14314         if(this.fireEvent("beforeload", this, params) !== false){
14315             var  o = {
14316                 params : params || {},
14317                 request: {
14318                     callback : callback,
14319                     scope : scope,
14320                     arg : arg
14321                 },
14322                 reader: reader,
14323                 callback : this.loadResponse,
14324                 scope: this
14325             };
14326             if(this.useAjax){
14327                 Roo.applyIf(o, this.conn);
14328                 if(this.activeRequest){
14329                     Roo.Ajax.abort(this.activeRequest);
14330                 }
14331                 this.activeRequest = Roo.Ajax.request(o);
14332             }else{
14333                 this.conn.request(o);
14334             }
14335         }else{
14336             callback.call(scope||this, null, arg, false);
14337         }
14338     },
14339
14340     // private
14341     loadResponse : function(o, success, response){
14342         delete this.activeRequest;
14343         if(!success){
14344             this.fireEvent("loadexception", this, o, response);
14345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14346             return;
14347         }
14348         var result;
14349         try {
14350             result = o.reader.read(response);
14351         }catch(e){
14352             this.fireEvent("loadexception", this, o, response, e);
14353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14354             return;
14355         }
14356         
14357         this.fireEvent("load", this, o, o.request.arg);
14358         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14359     },
14360
14361     // private
14362     update : function(dataSet){
14363
14364     },
14365
14366     // private
14367     updateResponse : function(dataSet){
14368
14369     }
14370 });/*
14371  * Based on:
14372  * Ext JS Library 1.1.1
14373  * Copyright(c) 2006-2007, Ext JS, LLC.
14374  *
14375  * Originally Released Under LGPL - original licence link has changed is not relivant.
14376  *
14377  * Fork - LGPL
14378  * <script type="text/javascript">
14379  */
14380
14381 /**
14382  * @class Roo.data.ScriptTagProxy
14383  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14384  * other than the originating domain of the running page.<br><br>
14385  * <p>
14386  * <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
14387  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14388  * <p>
14389  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14390  * source code that is used as the source inside a &lt;script> tag.<br><br>
14391  * <p>
14392  * In order for the browser to process the returned data, the server must wrap the data object
14393  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14394  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14395  * depending on whether the callback name was passed:
14396  * <p>
14397  * <pre><code>
14398 boolean scriptTag = false;
14399 String cb = request.getParameter("callback");
14400 if (cb != null) {
14401     scriptTag = true;
14402     response.setContentType("text/javascript");
14403 } else {
14404     response.setContentType("application/x-json");
14405 }
14406 Writer out = response.getWriter();
14407 if (scriptTag) {
14408     out.write(cb + "(");
14409 }
14410 out.print(dataBlock.toJsonString());
14411 if (scriptTag) {
14412     out.write(");");
14413 }
14414 </pre></code>
14415  *
14416  * @constructor
14417  * @param {Object} config A configuration object.
14418  */
14419 Roo.data.ScriptTagProxy = function(config){
14420     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14421     Roo.apply(this, config);
14422     this.head = document.getElementsByTagName("head")[0];
14423 };
14424
14425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14426
14427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14428     /**
14429      * @cfg {String} url The URL from which to request the data object.
14430      */
14431     /**
14432      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14433      */
14434     timeout : 30000,
14435     /**
14436      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14437      * the server the name of the callback function set up by the load call to process the returned data object.
14438      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14439      * javascript output which calls this named function passing the data object as its only parameter.
14440      */
14441     callbackParam : "callback",
14442     /**
14443      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14444      * name to the request.
14445      */
14446     nocache : true,
14447
14448     /**
14449      * Load data from the configured URL, read the data object into
14450      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14451      * process that block using the passed callback.
14452      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14453      * for the request to the remote server.
14454      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14455      * object into a block of Roo.data.Records.
14456      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14457      * The function must be passed <ul>
14458      * <li>The Record block object</li>
14459      * <li>The "arg" argument from the load function</li>
14460      * <li>A boolean success indicator</li>
14461      * </ul>
14462      * @param {Object} scope The scope in which to call the callback
14463      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14464      */
14465     load : function(params, reader, callback, scope, arg){
14466         if(this.fireEvent("beforeload", this, params) !== false){
14467
14468             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14469
14470             var url = this.url;
14471             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14472             if(this.nocache){
14473                 url += "&_dc=" + (new Date().getTime());
14474             }
14475             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14476             var trans = {
14477                 id : transId,
14478                 cb : "stcCallback"+transId,
14479                 scriptId : "stcScript"+transId,
14480                 params : params,
14481                 arg : arg,
14482                 url : url,
14483                 callback : callback,
14484                 scope : scope,
14485                 reader : reader
14486             };
14487             var conn = this;
14488
14489             window[trans.cb] = function(o){
14490                 conn.handleResponse(o, trans);
14491             };
14492
14493             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14494
14495             if(this.autoAbort !== false){
14496                 this.abort();
14497             }
14498
14499             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14500
14501             var script = document.createElement("script");
14502             script.setAttribute("src", url);
14503             script.setAttribute("type", "text/javascript");
14504             script.setAttribute("id", trans.scriptId);
14505             this.head.appendChild(script);
14506
14507             this.trans = trans;
14508         }else{
14509             callback.call(scope||this, null, arg, false);
14510         }
14511     },
14512
14513     // private
14514     isLoading : function(){
14515         return this.trans ? true : false;
14516     },
14517
14518     /**
14519      * Abort the current server request.
14520      */
14521     abort : function(){
14522         if(this.isLoading()){
14523             this.destroyTrans(this.trans);
14524         }
14525     },
14526
14527     // private
14528     destroyTrans : function(trans, isLoaded){
14529         this.head.removeChild(document.getElementById(trans.scriptId));
14530         clearTimeout(trans.timeoutId);
14531         if(isLoaded){
14532             window[trans.cb] = undefined;
14533             try{
14534                 delete window[trans.cb];
14535             }catch(e){}
14536         }else{
14537             // if hasn't been loaded, wait for load to remove it to prevent script error
14538             window[trans.cb] = function(){
14539                 window[trans.cb] = undefined;
14540                 try{
14541                     delete window[trans.cb];
14542                 }catch(e){}
14543             };
14544         }
14545     },
14546
14547     // private
14548     handleResponse : function(o, trans){
14549         this.trans = false;
14550         this.destroyTrans(trans, true);
14551         var result;
14552         try {
14553             result = trans.reader.readRecords(o);
14554         }catch(e){
14555             this.fireEvent("loadexception", this, o, trans.arg, e);
14556             trans.callback.call(trans.scope||window, null, trans.arg, false);
14557             return;
14558         }
14559         this.fireEvent("load", this, o, trans.arg);
14560         trans.callback.call(trans.scope||window, result, trans.arg, true);
14561     },
14562
14563     // private
14564     handleFailure : function(trans){
14565         this.trans = false;
14566         this.destroyTrans(trans, false);
14567         this.fireEvent("loadexception", this, null, trans.arg);
14568         trans.callback.call(trans.scope||window, null, trans.arg, false);
14569     }
14570 });/*
14571  * Based on:
14572  * Ext JS Library 1.1.1
14573  * Copyright(c) 2006-2007, Ext JS, LLC.
14574  *
14575  * Originally Released Under LGPL - original licence link has changed is not relivant.
14576  *
14577  * Fork - LGPL
14578  * <script type="text/javascript">
14579  */
14580
14581 /**
14582  * @class Roo.data.JsonReader
14583  * @extends Roo.data.DataReader
14584  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14585  * based on mappings in a provided Roo.data.Record constructor.
14586  * 
14587  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14588  * in the reply previously. 
14589  * 
14590  * <p>
14591  * Example code:
14592  * <pre><code>
14593 var RecordDef = Roo.data.Record.create([
14594     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14595     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14596 ]);
14597 var myReader = new Roo.data.JsonReader({
14598     totalProperty: "results",    // The property which contains the total dataset size (optional)
14599     root: "rows",                // The property which contains an Array of row objects
14600     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14601 }, RecordDef);
14602 </code></pre>
14603  * <p>
14604  * This would consume a JSON file like this:
14605  * <pre><code>
14606 { 'results': 2, 'rows': [
14607     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14608     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14609 }
14610 </code></pre>
14611  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14612  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14613  * paged from the remote server.
14614  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14615  * @cfg {String} root name of the property which contains the Array of row objects.
14616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14617  * @cfg {Array} fields Array of field definition objects
14618  * @constructor
14619  * Create a new JsonReader
14620  * @param {Object} meta Metadata configuration options
14621  * @param {Object} recordType Either an Array of field definition objects,
14622  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14623  */
14624 Roo.data.JsonReader = function(meta, recordType){
14625     
14626     meta = meta || {};
14627     // set some defaults:
14628     Roo.applyIf(meta, {
14629         totalProperty: 'total',
14630         successProperty : 'success',
14631         root : 'data',
14632         id : 'id'
14633     });
14634     
14635     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14636 };
14637 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14638     
14639     readerType : 'Json',
14640     
14641     /**
14642      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14643      * Used by Store query builder to append _requestMeta to params.
14644      * 
14645      */
14646     metaFromRemote : false,
14647     /**
14648      * This method is only used by a DataProxy which has retrieved data from a remote server.
14649      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14650      * @return {Object} data A data block which is used by an Roo.data.Store object as
14651      * a cache of Roo.data.Records.
14652      */
14653     read : function(response){
14654         var json = response.responseText;
14655        
14656         var o = /* eval:var:o */ eval("("+json+")");
14657         if(!o) {
14658             throw {message: "JsonReader.read: Json object not found"};
14659         }
14660         
14661         if(o.metaData){
14662             
14663             delete this.ef;
14664             this.metaFromRemote = true;
14665             this.meta = o.metaData;
14666             this.recordType = Roo.data.Record.create(o.metaData.fields);
14667             this.onMetaChange(this.meta, this.recordType, o);
14668         }
14669         return this.readRecords(o);
14670     },
14671
14672     // private function a store will implement
14673     onMetaChange : function(meta, recordType, o){
14674
14675     },
14676
14677     /**
14678          * @ignore
14679          */
14680     simpleAccess: function(obj, subsc) {
14681         return obj[subsc];
14682     },
14683
14684         /**
14685          * @ignore
14686          */
14687     getJsonAccessor: function(){
14688         var re = /[\[\.]/;
14689         return function(expr) {
14690             try {
14691                 return(re.test(expr))
14692                     ? new Function("obj", "return obj." + expr)
14693                     : function(obj){
14694                         return obj[expr];
14695                     };
14696             } catch(e){}
14697             return Roo.emptyFn;
14698         };
14699     }(),
14700
14701     /**
14702      * Create a data block containing Roo.data.Records from an XML document.
14703      * @param {Object} o An object which contains an Array of row objects in the property specified
14704      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14705      * which contains the total size of the dataset.
14706      * @return {Object} data A data block which is used by an Roo.data.Store object as
14707      * a cache of Roo.data.Records.
14708      */
14709     readRecords : function(o){
14710         /**
14711          * After any data loads, the raw JSON data is available for further custom processing.
14712          * @type Object
14713          */
14714         this.o = o;
14715         var s = this.meta, Record = this.recordType,
14716             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14717
14718 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14719         if (!this.ef) {
14720             if(s.totalProperty) {
14721                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14722                 }
14723                 if(s.successProperty) {
14724                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14725                 }
14726                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14727                 if (s.id) {
14728                         var g = this.getJsonAccessor(s.id);
14729                         this.getId = function(rec) {
14730                                 var r = g(rec);  
14731                                 return (r === undefined || r === "") ? null : r;
14732                         };
14733                 } else {
14734                         this.getId = function(){return null;};
14735                 }
14736             this.ef = [];
14737             for(var jj = 0; jj < fl; jj++){
14738                 f = fi[jj];
14739                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14740                 this.ef[jj] = this.getJsonAccessor(map);
14741             }
14742         }
14743
14744         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14745         if(s.totalProperty){
14746             var vt = parseInt(this.getTotal(o), 10);
14747             if(!isNaN(vt)){
14748                 totalRecords = vt;
14749             }
14750         }
14751         if(s.successProperty){
14752             var vs = this.getSuccess(o);
14753             if(vs === false || vs === 'false'){
14754                 success = false;
14755             }
14756         }
14757         var records = [];
14758         for(var i = 0; i < c; i++){
14759                 var n = root[i];
14760             var values = {};
14761             var id = this.getId(n);
14762             for(var j = 0; j < fl; j++){
14763                 f = fi[j];
14764             var v = this.ef[j](n);
14765             if (!f.convert) {
14766                 Roo.log('missing convert for ' + f.name);
14767                 Roo.log(f);
14768                 continue;
14769             }
14770             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14771             }
14772             var record = new Record(values, id);
14773             record.json = n;
14774             records[i] = record;
14775         }
14776         return {
14777             raw : o,
14778             success : success,
14779             records : records,
14780             totalRecords : totalRecords
14781         };
14782     },
14783     // used when loading children.. @see loadDataFromChildren
14784     toLoadData: function(rec)
14785     {
14786         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14787         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14788         return { data : data, total : data.length };
14789         
14790     }
14791 });/*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801
14802 /**
14803  * @class Roo.data.ArrayReader
14804  * @extends Roo.data.DataReader
14805  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14806  * Each element of that Array represents a row of data fields. The
14807  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14808  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14809  * <p>
14810  * Example code:.
14811  * <pre><code>
14812 var RecordDef = Roo.data.Record.create([
14813     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14814     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14815 ]);
14816 var myReader = new Roo.data.ArrayReader({
14817     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14818 }, RecordDef);
14819 </code></pre>
14820  * <p>
14821  * This would consume an Array like this:
14822  * <pre><code>
14823 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14824   </code></pre>
14825  
14826  * @constructor
14827  * Create a new JsonReader
14828  * @param {Object} meta Metadata configuration options.
14829  * @param {Object|Array} recordType Either an Array of field definition objects
14830  * 
14831  * @cfg {Array} fields Array of field definition objects
14832  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14833  * as specified to {@link Roo.data.Record#create},
14834  * or an {@link Roo.data.Record} object
14835  *
14836  * 
14837  * created using {@link Roo.data.Record#create}.
14838  */
14839 Roo.data.ArrayReader = function(meta, recordType)
14840 {    
14841     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14842 };
14843
14844 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14845     
14846       /**
14847      * Create a data block containing Roo.data.Records from an XML document.
14848      * @param {Object} o An Array of row objects which represents the dataset.
14849      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14850      * a cache of Roo.data.Records.
14851      */
14852     readRecords : function(o)
14853     {
14854         var sid = this.meta ? this.meta.id : null;
14855         var recordType = this.recordType, fields = recordType.prototype.fields;
14856         var records = [];
14857         var root = o;
14858         for(var i = 0; i < root.length; i++){
14859                 var n = root[i];
14860             var values = {};
14861             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14862             for(var j = 0, jlen = fields.length; j < jlen; j++){
14863                 var f = fields.items[j];
14864                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14865                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14866                 v = f.convert(v);
14867                 values[f.name] = v;
14868             }
14869             var record = new recordType(values, id);
14870             record.json = n;
14871             records[records.length] = record;
14872         }
14873         return {
14874             records : records,
14875             totalRecords : records.length
14876         };
14877     },
14878     // used when loading children.. @see loadDataFromChildren
14879     toLoadData: function(rec)
14880     {
14881         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14882         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14883         
14884     }
14885     
14886     
14887 });/*
14888  * - LGPL
14889  * * 
14890  */
14891
14892 /**
14893  * @class Roo.bootstrap.ComboBox
14894  * @extends Roo.bootstrap.TriggerField
14895  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14896  * @cfg {Boolean} append (true|false) default false
14897  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14898  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14899  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14900  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14901  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14902  * @cfg {Boolean} animate default true
14903  * @cfg {Boolean} emptyResultText only for touch device
14904  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14905  * @cfg {String} emptyTitle default ''
14906  * @cfg {Number} width fixed with? experimental
14907  * @constructor
14908  * Create a new ComboBox.
14909  * @param {Object} config Configuration options
14910  */
14911 Roo.bootstrap.ComboBox = function(config){
14912     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14913     this.addEvents({
14914         /**
14915          * @event expand
14916          * Fires when the dropdown list is expanded
14917         * @param {Roo.bootstrap.ComboBox} combo This combo box
14918         */
14919         'expand' : true,
14920         /**
14921          * @event collapse
14922          * Fires when the dropdown list is collapsed
14923         * @param {Roo.bootstrap.ComboBox} combo This combo box
14924         */
14925         'collapse' : true,
14926         /**
14927          * @event beforeselect
14928          * Fires before a list item is selected. Return false to cancel the selection.
14929         * @param {Roo.bootstrap.ComboBox} combo This combo box
14930         * @param {Roo.data.Record} record The data record returned from the underlying store
14931         * @param {Number} index The index of the selected item in the dropdown list
14932         */
14933         'beforeselect' : true,
14934         /**
14935          * @event select
14936          * Fires when a list item is selected
14937         * @param {Roo.bootstrap.ComboBox} combo This combo box
14938         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14939         * @param {Number} index The index of the selected item in the dropdown list
14940         */
14941         'select' : true,
14942         /**
14943          * @event beforequery
14944          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14945          * The event object passed has these properties:
14946         * @param {Roo.bootstrap.ComboBox} combo This combo box
14947         * @param {String} query The query
14948         * @param {Boolean} forceAll true to force "all" query
14949         * @param {Boolean} cancel true to cancel the query
14950         * @param {Object} e The query event object
14951         */
14952         'beforequery': true,
14953          /**
14954          * @event add
14955          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14956         * @param {Roo.bootstrap.ComboBox} combo This combo box
14957         */
14958         'add' : true,
14959         /**
14960          * @event edit
14961          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14962         * @param {Roo.bootstrap.ComboBox} combo This combo box
14963         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14964         */
14965         'edit' : true,
14966         /**
14967          * @event remove
14968          * Fires when the remove value from the combobox array
14969         * @param {Roo.bootstrap.ComboBox} combo This combo box
14970         */
14971         'remove' : true,
14972         /**
14973          * @event afterremove
14974          * Fires when the remove value from the combobox array
14975         * @param {Roo.bootstrap.ComboBox} combo This combo box
14976         */
14977         'afterremove' : true,
14978         /**
14979          * @event specialfilter
14980          * Fires when specialfilter
14981             * @param {Roo.bootstrap.ComboBox} combo This combo box
14982             */
14983         'specialfilter' : true,
14984         /**
14985          * @event tick
14986          * Fires when tick the element
14987             * @param {Roo.bootstrap.ComboBox} combo This combo box
14988             */
14989         'tick' : true,
14990         /**
14991          * @event touchviewdisplay
14992          * Fires when touch view require special display (default is using displayField)
14993             * @param {Roo.bootstrap.ComboBox} combo This combo box
14994             * @param {Object} cfg set html .
14995             */
14996         'touchviewdisplay' : true
14997         
14998     });
14999     
15000     this.item = [];
15001     this.tickItems = [];
15002     
15003     this.selectedIndex = -1;
15004     if(this.mode == 'local'){
15005         if(config.queryDelay === undefined){
15006             this.queryDelay = 10;
15007         }
15008         if(config.minChars === undefined){
15009             this.minChars = 0;
15010         }
15011     }
15012 };
15013
15014 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15015      
15016     /**
15017      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15018      * rendering into an Roo.Editor, defaults to false)
15019      */
15020     /**
15021      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15022      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15023      */
15024     /**
15025      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15026      */
15027     /**
15028      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15029      * the dropdown list (defaults to undefined, with no header element)
15030      */
15031
15032      /**
15033      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15034      */
15035      
15036      /**
15037      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15038      */
15039     listWidth: undefined,
15040     /**
15041      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15042      * mode = 'remote' or 'text' if mode = 'local')
15043      */
15044     displayField: undefined,
15045     
15046     /**
15047      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15048      * mode = 'remote' or 'value' if mode = 'local'). 
15049      * Note: use of a valueField requires the user make a selection
15050      * in order for a value to be mapped.
15051      */
15052     valueField: undefined,
15053     /**
15054      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15055      */
15056     modalTitle : '',
15057     
15058     /**
15059      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15060      * field's data value (defaults to the underlying DOM element's name)
15061      */
15062     hiddenName: undefined,
15063     /**
15064      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15065      */
15066     listClass: '',
15067     /**
15068      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15069      */
15070     selectedClass: 'active',
15071     
15072     /**
15073      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15074      */
15075     shadow:'sides',
15076     /**
15077      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15078      * anchor positions (defaults to 'tl-bl')
15079      */
15080     listAlign: 'tl-bl?',
15081     /**
15082      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15083      */
15084     maxHeight: 300,
15085     /**
15086      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15087      * query specified by the allQuery config option (defaults to 'query')
15088      */
15089     triggerAction: 'query',
15090     /**
15091      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15092      * (defaults to 4, does not apply if editable = false)
15093      */
15094     minChars : 4,
15095     /**
15096      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15097      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15098      */
15099     typeAhead: false,
15100     /**
15101      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15102      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15103      */
15104     queryDelay: 500,
15105     /**
15106      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15107      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15108      */
15109     pageSize: 0,
15110     /**
15111      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15112      * when editable = true (defaults to false)
15113      */
15114     selectOnFocus:false,
15115     /**
15116      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15117      */
15118     queryParam: 'query',
15119     /**
15120      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15121      * when mode = 'remote' (defaults to 'Loading...')
15122      */
15123     loadingText: 'Loading...',
15124     /**
15125      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15126      */
15127     resizable: false,
15128     /**
15129      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15130      */
15131     handleHeight : 8,
15132     /**
15133      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15134      * traditional select (defaults to true)
15135      */
15136     editable: true,
15137     /**
15138      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15139      */
15140     allQuery: '',
15141     /**
15142      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15143      */
15144     mode: 'remote',
15145     /**
15146      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15147      * listWidth has a higher value)
15148      */
15149     minListWidth : 70,
15150     /**
15151      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15152      * allow the user to set arbitrary text into the field (defaults to false)
15153      */
15154     forceSelection:false,
15155     /**
15156      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15157      * if typeAhead = true (defaults to 250)
15158      */
15159     typeAheadDelay : 250,
15160     /**
15161      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15162      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15163      */
15164     valueNotFoundText : undefined,
15165     /**
15166      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15167      */
15168     blockFocus : false,
15169     
15170     /**
15171      * @cfg {Boolean} disableClear Disable showing of clear button.
15172      */
15173     disableClear : false,
15174     /**
15175      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15176      */
15177     alwaysQuery : false,
15178     
15179     /**
15180      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15181      */
15182     multiple : false,
15183     
15184     /**
15185      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15186      */
15187     invalidClass : "has-warning",
15188     
15189     /**
15190      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15191      */
15192     validClass : "has-success",
15193     
15194     /**
15195      * @cfg {Boolean} specialFilter (true|false) special filter default false
15196      */
15197     specialFilter : false,
15198     
15199     /**
15200      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15201      */
15202     mobileTouchView : true,
15203     
15204     /**
15205      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15206      */
15207     useNativeIOS : false,
15208     
15209     /**
15210      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15211      */
15212     mobile_restrict_height : false,
15213     
15214     ios_options : false,
15215     
15216     //private
15217     addicon : false,
15218     editicon: false,
15219     
15220     page: 0,
15221     hasQuery: false,
15222     append: false,
15223     loadNext: false,
15224     autoFocus : true,
15225     tickable : false,
15226     btnPosition : 'right',
15227     triggerList : true,
15228     showToggleBtn : true,
15229     animate : true,
15230     emptyResultText: 'Empty',
15231     triggerText : 'Select',
15232     emptyTitle : '',
15233     width : false,
15234     
15235     // element that contains real text value.. (when hidden is used..)
15236     
15237     getAutoCreate : function()
15238     {   
15239         var cfg = false;
15240         //render
15241         /*
15242          * Render classic select for iso
15243          */
15244         
15245         if(Roo.isIOS && this.useNativeIOS){
15246             cfg = this.getAutoCreateNativeIOS();
15247             return cfg;
15248         }
15249         
15250         /*
15251          * Touch Devices
15252          */
15253         
15254         if(Roo.isTouch && this.mobileTouchView){
15255             cfg = this.getAutoCreateTouchView();
15256             return cfg;;
15257         }
15258         
15259         /*
15260          *  Normal ComboBox
15261          */
15262         if(!this.tickable){
15263             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15264             return cfg;
15265         }
15266         
15267         /*
15268          *  ComboBox with tickable selections
15269          */
15270              
15271         var align = this.labelAlign || this.parentLabelAlign();
15272         
15273         cfg = {
15274             cls : 'form-group roo-combobox-tickable' //input-group
15275         };
15276         
15277         var btn_text_select = '';
15278         var btn_text_done = '';
15279         var btn_text_cancel = '';
15280         
15281         if (this.btn_text_show) {
15282             btn_text_select = 'Select';
15283             btn_text_done = 'Done';
15284             btn_text_cancel = 'Cancel'; 
15285         }
15286         
15287         var buttons = {
15288             tag : 'div',
15289             cls : 'tickable-buttons',
15290             cn : [
15291                 {
15292                     tag : 'button',
15293                     type : 'button',
15294                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15295                     //html : this.triggerText
15296                     html: btn_text_select
15297                 },
15298                 {
15299                     tag : 'button',
15300                     type : 'button',
15301                     name : 'ok',
15302                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15303                     //html : 'Done'
15304                     html: btn_text_done
15305                 },
15306                 {
15307                     tag : 'button',
15308                     type : 'button',
15309                     name : 'cancel',
15310                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15311                     //html : 'Cancel'
15312                     html: btn_text_cancel
15313                 }
15314             ]
15315         };
15316         
15317         if(this.editable){
15318             buttons.cn.unshift({
15319                 tag: 'input',
15320                 cls: 'roo-select2-search-field-input'
15321             });
15322         }
15323         
15324         var _this = this;
15325         
15326         Roo.each(buttons.cn, function(c){
15327             if (_this.size) {
15328                 c.cls += ' btn-' + _this.size;
15329             }
15330
15331             if (_this.disabled) {
15332                 c.disabled = true;
15333             }
15334         });
15335         
15336         var box = {
15337             tag: 'div',
15338             style : 'display: contents',
15339             cn: [
15340                 {
15341                     tag: 'input',
15342                     type : 'hidden',
15343                     cls: 'form-hidden-field'
15344                 },
15345                 {
15346                     tag: 'ul',
15347                     cls: 'roo-select2-choices',
15348                     cn:[
15349                         {
15350                             tag: 'li',
15351                             cls: 'roo-select2-search-field',
15352                             cn: [
15353                                 buttons
15354                             ]
15355                         }
15356                     ]
15357                 }
15358             ]
15359         };
15360         
15361         var combobox = {
15362             cls: 'roo-select2-container input-group roo-select2-container-multi',
15363             cn: [
15364                 
15365                 box
15366 //                {
15367 //                    tag: 'ul',
15368 //                    cls: 'typeahead typeahead-long dropdown-menu',
15369 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15370 //                }
15371             ]
15372         };
15373         
15374         if(this.hasFeedback && !this.allowBlank){
15375             
15376             var feedback = {
15377                 tag: 'span',
15378                 cls: 'glyphicon form-control-feedback'
15379             };
15380
15381             combobox.cn.push(feedback);
15382         }
15383         
15384         
15385         
15386         var indicator = {
15387             tag : 'i',
15388             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15389             tooltip : 'This field is required'
15390         };
15391         if (Roo.bootstrap.version == 4) {
15392             indicator = {
15393                 tag : 'i',
15394                 style : 'display:none'
15395             };
15396         }
15397         if (align ==='left' && this.fieldLabel.length) {
15398             
15399             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15400             
15401             cfg.cn = [
15402                 indicator,
15403                 {
15404                     tag: 'label',
15405                     'for' :  id,
15406                     cls : 'control-label col-form-label',
15407                     html : this.fieldLabel
15408
15409                 },
15410                 {
15411                     cls : "", 
15412                     cn: [
15413                         combobox
15414                     ]
15415                 }
15416
15417             ];
15418             
15419             var labelCfg = cfg.cn[1];
15420             var contentCfg = cfg.cn[2];
15421             
15422
15423             if(this.indicatorpos == 'right'){
15424                 
15425                 cfg.cn = [
15426                     {
15427                         tag: 'label',
15428                         'for' :  id,
15429                         cls : 'control-label col-form-label',
15430                         cn : [
15431                             {
15432                                 tag : 'span',
15433                                 html : this.fieldLabel
15434                             },
15435                             indicator
15436                         ]
15437                     },
15438                     {
15439                         cls : "",
15440                         cn: [
15441                             combobox
15442                         ]
15443                     }
15444
15445                 ];
15446                 
15447                 
15448                 
15449                 labelCfg = cfg.cn[0];
15450                 contentCfg = cfg.cn[1];
15451             
15452             }
15453             
15454             if(this.labelWidth > 12){
15455                 labelCfg.style = "width: " + this.labelWidth + 'px';
15456             }
15457             if(this.width * 1 > 0){
15458                 contentCfg.style = "width: " + this.width + 'px';
15459             }
15460             if(this.labelWidth < 13 && this.labelmd == 0){
15461                 this.labelmd = this.labelWidth;
15462             }
15463             
15464             if(this.labellg > 0){
15465                 labelCfg.cls += ' col-lg-' + this.labellg;
15466                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15467             }
15468             
15469             if(this.labelmd > 0){
15470                 labelCfg.cls += ' col-md-' + this.labelmd;
15471                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15472             }
15473             
15474             if(this.labelsm > 0){
15475                 labelCfg.cls += ' col-sm-' + this.labelsm;
15476                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15477             }
15478             
15479             if(this.labelxs > 0){
15480                 labelCfg.cls += ' col-xs-' + this.labelxs;
15481                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15482             }
15483                 
15484                 
15485         } else if ( this.fieldLabel.length) {
15486 //                Roo.log(" label");
15487                  cfg.cn = [
15488                    indicator,
15489                     {
15490                         tag: 'label',
15491                         //cls : 'input-group-addon',
15492                         html : this.fieldLabel
15493                     },
15494                     combobox
15495                 ];
15496                 
15497                 if(this.indicatorpos == 'right'){
15498                     cfg.cn = [
15499                         {
15500                             tag: 'label',
15501                             //cls : 'input-group-addon',
15502                             html : this.fieldLabel
15503                         },
15504                         indicator,
15505                         combobox
15506                     ];
15507                     
15508                 }
15509
15510         } else {
15511             
15512 //                Roo.log(" no label && no align");
15513                 cfg = combobox
15514                      
15515                 
15516         }
15517          
15518         var settings=this;
15519         ['xs','sm','md','lg'].map(function(size){
15520             if (settings[size]) {
15521                 cfg.cls += ' col-' + size + '-' + settings[size];
15522             }
15523         });
15524         
15525         return cfg;
15526         
15527     },
15528     
15529     _initEventsCalled : false,
15530     
15531     // private
15532     initEvents: function()
15533     {   
15534         if (this._initEventsCalled) { // as we call render... prevent looping...
15535             return;
15536         }
15537         this._initEventsCalled = true;
15538         
15539         if (!this.store) {
15540             throw "can not find store for combo";
15541         }
15542         
15543         this.indicator = this.indicatorEl();
15544         
15545         this.store = Roo.factory(this.store, Roo.data);
15546         this.store.parent = this;
15547         
15548         // if we are building from html. then this element is so complex, that we can not really
15549         // use the rendered HTML.
15550         // so we have to trash and replace the previous code.
15551         if (Roo.XComponent.build_from_html) {
15552             // remove this element....
15553             var e = this.el.dom, k=0;
15554             while (e ) { e = e.previousSibling;  ++k;}
15555
15556             this.el.remove();
15557             
15558             this.el=false;
15559             this.rendered = false;
15560             
15561             this.render(this.parent().getChildContainer(true), k);
15562         }
15563         
15564         if(Roo.isIOS && this.useNativeIOS){
15565             this.initIOSView();
15566             return;
15567         }
15568         
15569         /*
15570          * Touch Devices
15571          */
15572         
15573         if(Roo.isTouch && this.mobileTouchView){
15574             this.initTouchView();
15575             return;
15576         }
15577         
15578         if(this.tickable){
15579             this.initTickableEvents();
15580             return;
15581         }
15582         
15583         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15584         
15585         if(this.hiddenName){
15586             
15587             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15588             
15589             this.hiddenField.dom.value =
15590                 this.hiddenValue !== undefined ? this.hiddenValue :
15591                 this.value !== undefined ? this.value : '';
15592
15593             // prevent input submission
15594             this.el.dom.removeAttribute('name');
15595             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15596              
15597              
15598         }
15599         //if(Roo.isGecko){
15600         //    this.el.dom.setAttribute('autocomplete', 'off');
15601         //}
15602         
15603         var cls = 'x-combo-list';
15604         
15605         //this.list = new Roo.Layer({
15606         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15607         //});
15608         
15609         var _this = this;
15610         
15611         (function(){
15612             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15613             _this.list.setWidth(lw);
15614         }).defer(100);
15615         
15616         this.list.on('mouseover', this.onViewOver, this);
15617         this.list.on('mousemove', this.onViewMove, this);
15618         this.list.on('scroll', this.onViewScroll, this);
15619         
15620         /*
15621         this.list.swallowEvent('mousewheel');
15622         this.assetHeight = 0;
15623
15624         if(this.title){
15625             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15626             this.assetHeight += this.header.getHeight();
15627         }
15628
15629         this.innerList = this.list.createChild({cls:cls+'-inner'});
15630         this.innerList.on('mouseover', this.onViewOver, this);
15631         this.innerList.on('mousemove', this.onViewMove, this);
15632         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15633         
15634         if(this.allowBlank && !this.pageSize && !this.disableClear){
15635             this.footer = this.list.createChild({cls:cls+'-ft'});
15636             this.pageTb = new Roo.Toolbar(this.footer);
15637            
15638         }
15639         if(this.pageSize){
15640             this.footer = this.list.createChild({cls:cls+'-ft'});
15641             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15642                     {pageSize: this.pageSize});
15643             
15644         }
15645         
15646         if (this.pageTb && this.allowBlank && !this.disableClear) {
15647             var _this = this;
15648             this.pageTb.add(new Roo.Toolbar.Fill(), {
15649                 cls: 'x-btn-icon x-btn-clear',
15650                 text: '&#160;',
15651                 handler: function()
15652                 {
15653                     _this.collapse();
15654                     _this.clearValue();
15655                     _this.onSelect(false, -1);
15656                 }
15657             });
15658         }
15659         if (this.footer) {
15660             this.assetHeight += this.footer.getHeight();
15661         }
15662         */
15663             
15664         if(!this.tpl){
15665             this.tpl = Roo.bootstrap.version == 4 ?
15666                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15667                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15668         }
15669
15670         this.view = new Roo.View(this.list, this.tpl, {
15671             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15672         });
15673         //this.view.wrapEl.setDisplayed(false);
15674         this.view.on('click', this.onViewClick, this);
15675         
15676         
15677         this.store.on('beforeload', this.onBeforeLoad, this);
15678         this.store.on('load', this.onLoad, this);
15679         this.store.on('loadexception', this.onLoadException, this);
15680         /*
15681         if(this.resizable){
15682             this.resizer = new Roo.Resizable(this.list,  {
15683                pinned:true, handles:'se'
15684             });
15685             this.resizer.on('resize', function(r, w, h){
15686                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15687                 this.listWidth = w;
15688                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15689                 this.restrictHeight();
15690             }, this);
15691             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15692         }
15693         */
15694         if(!this.editable){
15695             this.editable = true;
15696             this.setEditable(false);
15697         }
15698         
15699         /*
15700         
15701         if (typeof(this.events.add.listeners) != 'undefined') {
15702             
15703             this.addicon = this.wrap.createChild(
15704                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15705        
15706             this.addicon.on('click', function(e) {
15707                 this.fireEvent('add', this);
15708             }, this);
15709         }
15710         if (typeof(this.events.edit.listeners) != 'undefined') {
15711             
15712             this.editicon = this.wrap.createChild(
15713                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15714             if (this.addicon) {
15715                 this.editicon.setStyle('margin-left', '40px');
15716             }
15717             this.editicon.on('click', function(e) {
15718                 
15719                 // we fire even  if inothing is selected..
15720                 this.fireEvent('edit', this, this.lastData );
15721                 
15722             }, this);
15723         }
15724         */
15725         
15726         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15727             "up" : function(e){
15728                 this.inKeyMode = true;
15729                 this.selectPrev();
15730             },
15731
15732             "down" : function(e){
15733                 if(!this.isExpanded()){
15734                     this.onTriggerClick();
15735                 }else{
15736                     this.inKeyMode = true;
15737                     this.selectNext();
15738                 }
15739             },
15740
15741             "enter" : function(e){
15742 //                this.onViewClick();
15743                 //return true;
15744                 this.collapse();
15745                 
15746                 if(this.fireEvent("specialkey", this, e)){
15747                     this.onViewClick(false);
15748                 }
15749                 
15750                 return true;
15751             },
15752
15753             "esc" : function(e){
15754                 this.collapse();
15755             },
15756
15757             "tab" : function(e){
15758                 this.collapse();
15759                 
15760                 if(this.fireEvent("specialkey", this, e)){
15761                     this.onViewClick(false);
15762                 }
15763                 
15764                 return true;
15765             },
15766
15767             scope : this,
15768
15769             doRelay : function(foo, bar, hname){
15770                 if(hname == 'down' || this.scope.isExpanded()){
15771                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15772                 }
15773                 return true;
15774             },
15775
15776             forceKeyDown: true
15777         });
15778         
15779         
15780         this.queryDelay = Math.max(this.queryDelay || 10,
15781                 this.mode == 'local' ? 10 : 250);
15782         
15783         
15784         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15785         
15786         if(this.typeAhead){
15787             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788         }
15789         if(this.editable !== false){
15790             this.inputEl().on("keyup", this.onKeyUp, this);
15791         }
15792         if(this.forceSelection){
15793             this.inputEl().on('blur', this.doForce, this);
15794         }
15795         
15796         if(this.multiple){
15797             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15798             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15799         }
15800     },
15801     
15802     initTickableEvents: function()
15803     {   
15804         this.createList();
15805         
15806         if(this.hiddenName){
15807             
15808             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15809             
15810             this.hiddenField.dom.value =
15811                 this.hiddenValue !== undefined ? this.hiddenValue :
15812                 this.value !== undefined ? this.value : '';
15813
15814             // prevent input submission
15815             this.el.dom.removeAttribute('name');
15816             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15817              
15818              
15819         }
15820         
15821 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15822         
15823         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15824         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15825         if(this.triggerList){
15826             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15827         }
15828          
15829         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15830         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15831         
15832         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15833         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15834         
15835         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15836         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15837         
15838         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15839         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15840         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15841         
15842         this.okBtn.hide();
15843         this.cancelBtn.hide();
15844         
15845         var _this = this;
15846         
15847         (function(){
15848             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15849             _this.list.setWidth(lw);
15850         }).defer(100);
15851         
15852         this.list.on('mouseover', this.onViewOver, this);
15853         this.list.on('mousemove', this.onViewMove, this);
15854         
15855         this.list.on('scroll', this.onViewScroll, this);
15856         
15857         if(!this.tpl){
15858             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15859                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15860         }
15861
15862         this.view = new Roo.View(this.list, this.tpl, {
15863             singleSelect:true,
15864             tickable:true,
15865             parent:this,
15866             store: this.store,
15867             selectedClass: this.selectedClass
15868         });
15869         
15870         //this.view.wrapEl.setDisplayed(false);
15871         this.view.on('click', this.onViewClick, this);
15872         
15873         
15874         
15875         this.store.on('beforeload', this.onBeforeLoad, this);
15876         this.store.on('load', this.onLoad, this);
15877         this.store.on('loadexception', this.onLoadException, this);
15878         
15879         if(this.editable){
15880             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15881                 "up" : function(e){
15882                     this.inKeyMode = true;
15883                     this.selectPrev();
15884                 },
15885
15886                 "down" : function(e){
15887                     this.inKeyMode = true;
15888                     this.selectNext();
15889                 },
15890
15891                 "enter" : function(e){
15892                     if(this.fireEvent("specialkey", this, e)){
15893                         this.onViewClick(false);
15894                     }
15895                     
15896                     return true;
15897                 },
15898
15899                 "esc" : function(e){
15900                     this.onTickableFooterButtonClick(e, false, false);
15901                 },
15902
15903                 "tab" : function(e){
15904                     this.fireEvent("specialkey", this, e);
15905                     
15906                     this.onTickableFooterButtonClick(e, false, false);
15907                     
15908                     return true;
15909                 },
15910
15911                 scope : this,
15912
15913                 doRelay : function(e, fn, key){
15914                     if(this.scope.isExpanded()){
15915                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15916                     }
15917                     return true;
15918                 },
15919
15920                 forceKeyDown: true
15921             });
15922         }
15923         
15924         this.queryDelay = Math.max(this.queryDelay || 10,
15925                 this.mode == 'local' ? 10 : 250);
15926         
15927         
15928         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15929         
15930         if(this.typeAhead){
15931             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15932         }
15933         
15934         if(this.editable !== false){
15935             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15936         }
15937         
15938         this.indicator = this.indicatorEl();
15939         
15940         if(this.indicator){
15941             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15942             this.indicator.hide();
15943         }
15944         
15945     },
15946
15947     onDestroy : function(){
15948         if(this.view){
15949             this.view.setStore(null);
15950             this.view.el.removeAllListeners();
15951             this.view.el.remove();
15952             this.view.purgeListeners();
15953         }
15954         if(this.list){
15955             this.list.dom.innerHTML  = '';
15956         }
15957         
15958         if(this.store){
15959             this.store.un('beforeload', this.onBeforeLoad, this);
15960             this.store.un('load', this.onLoad, this);
15961             this.store.un('loadexception', this.onLoadException, this);
15962         }
15963         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15964     },
15965
15966     // private
15967     fireKey : function(e){
15968         if(e.isNavKeyPress() && !this.list.isVisible()){
15969             this.fireEvent("specialkey", this, e);
15970         }
15971     },
15972
15973     // private
15974     onResize: function(w, h)
15975     {
15976         
15977         
15978 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15979 //        
15980 //        if(typeof w != 'number'){
15981 //            // we do not handle it!?!?
15982 //            return;
15983 //        }
15984 //        var tw = this.trigger.getWidth();
15985 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15986 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15987 //        var x = w - tw;
15988 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15989 //            
15990 //        //this.trigger.setStyle('left', x+'px');
15991 //        
15992 //        if(this.list && this.listWidth === undefined){
15993 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15994 //            this.list.setWidth(lw);
15995 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15996 //        }
15997         
15998     
15999         
16000     },
16001
16002     /**
16003      * Allow or prevent the user from directly editing the field text.  If false is passed,
16004      * the user will only be able to select from the items defined in the dropdown list.  This method
16005      * is the runtime equivalent of setting the 'editable' config option at config time.
16006      * @param {Boolean} value True to allow the user to directly edit the field text
16007      */
16008     setEditable : function(value){
16009         if(value == this.editable){
16010             return;
16011         }
16012         this.editable = value;
16013         if(!value){
16014             this.inputEl().dom.setAttribute('readOnly', true);
16015             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16016             this.inputEl().addClass('x-combo-noedit');
16017         }else{
16018             this.inputEl().dom.setAttribute('readOnly', false);
16019             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16020             this.inputEl().removeClass('x-combo-noedit');
16021         }
16022     },
16023
16024     // private
16025     
16026     onBeforeLoad : function(combo,opts){
16027         if(!this.hasFocus){
16028             return;
16029         }
16030          if (!opts.add) {
16031             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16032          }
16033         this.restrictHeight();
16034         this.selectedIndex = -1;
16035     },
16036
16037     // private
16038     onLoad : function(){
16039         
16040         this.hasQuery = false;
16041         
16042         if(!this.hasFocus){
16043             return;
16044         }
16045         
16046         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16047             this.loading.hide();
16048         }
16049         
16050         if(this.store.getCount() > 0){
16051             
16052             this.expand();
16053             this.restrictHeight();
16054             if(this.lastQuery == this.allQuery){
16055                 if(this.editable && !this.tickable){
16056                     this.inputEl().dom.select();
16057                 }
16058                 
16059                 if(
16060                     !this.selectByValue(this.value, true) &&
16061                     this.autoFocus && 
16062                     (
16063                         !this.store.lastOptions ||
16064                         typeof(this.store.lastOptions.add) == 'undefined' || 
16065                         this.store.lastOptions.add != true
16066                     )
16067                 ){
16068                     this.select(0, true);
16069                 }
16070             }else{
16071                 if(this.autoFocus){
16072                     this.selectNext();
16073                 }
16074                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16075                     this.taTask.delay(this.typeAheadDelay);
16076                 }
16077             }
16078         }else{
16079             this.onEmptyResults();
16080         }
16081         
16082         //this.el.focus();
16083     },
16084     // private
16085     onLoadException : function()
16086     {
16087         this.hasQuery = false;
16088         
16089         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16090             this.loading.hide();
16091         }
16092         
16093         if(this.tickable && this.editable){
16094             return;
16095         }
16096         
16097         this.collapse();
16098         // only causes errors at present
16099         //Roo.log(this.store.reader.jsonData);
16100         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16101             // fixme
16102             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16103         //}
16104         
16105         
16106     },
16107     // private
16108     onTypeAhead : function(){
16109         if(this.store.getCount() > 0){
16110             var r = this.store.getAt(0);
16111             var newValue = r.data[this.displayField];
16112             var len = newValue.length;
16113             var selStart = this.getRawValue().length;
16114             
16115             if(selStart != len){
16116                 this.setRawValue(newValue);
16117                 this.selectText(selStart, newValue.length);
16118             }
16119         }
16120     },
16121
16122     // private
16123     onSelect : function(record, index){
16124         
16125         if(this.fireEvent('beforeselect', this, record, index) !== false){
16126         
16127             this.setFromData(index > -1 ? record.data : false);
16128             
16129             this.collapse();
16130             this.fireEvent('select', this, record, index);
16131         }
16132     },
16133
16134     /**
16135      * Returns the currently selected field value or empty string if no value is set.
16136      * @return {String} value The selected value
16137      */
16138     getValue : function()
16139     {
16140         if(Roo.isIOS && this.useNativeIOS){
16141             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16142         }
16143         
16144         if(this.multiple){
16145             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16146         }
16147         
16148         if(this.valueField){
16149             return typeof this.value != 'undefined' ? this.value : '';
16150         }else{
16151             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16152         }
16153     },
16154     
16155     getRawValue : function()
16156     {
16157         if(Roo.isIOS && this.useNativeIOS){
16158             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16159         }
16160         
16161         var v = this.inputEl().getValue();
16162         
16163         return v;
16164     },
16165
16166     /**
16167      * Clears any text/value currently set in the field
16168      */
16169     clearValue : function(){
16170         
16171         if(this.hiddenField){
16172             this.hiddenField.dom.value = '';
16173         }
16174         this.value = '';
16175         this.setRawValue('');
16176         this.lastSelectionText = '';
16177         this.lastData = false;
16178         
16179         var close = this.closeTriggerEl();
16180         
16181         if(close){
16182             close.hide();
16183         }
16184         
16185         this.validate();
16186         
16187     },
16188
16189     /**
16190      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16191      * will be displayed in the field.  If the value does not match the data value of an existing item,
16192      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16193      * Otherwise the field will be blank (although the value will still be set).
16194      * @param {String} value The value to match
16195      */
16196     setValue : function(v)
16197     {
16198         if(Roo.isIOS && this.useNativeIOS){
16199             this.setIOSValue(v);
16200             return;
16201         }
16202         
16203         if(this.multiple){
16204             this.syncValue();
16205             return;
16206         }
16207         
16208         var text = v;
16209         if(this.valueField){
16210             var r = this.findRecord(this.valueField, v);
16211             if(r){
16212                 text = r.data[this.displayField];
16213             }else if(this.valueNotFoundText !== undefined){
16214                 text = this.valueNotFoundText;
16215             }
16216         }
16217         this.lastSelectionText = text;
16218         if(this.hiddenField){
16219             this.hiddenField.dom.value = v;
16220         }
16221         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16222         this.value = v;
16223         
16224         var close = this.closeTriggerEl();
16225         
16226         if(close){
16227             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16228         }
16229         
16230         this.validate();
16231     },
16232     /**
16233      * @property {Object} the last set data for the element
16234      */
16235     
16236     lastData : false,
16237     /**
16238      * Sets the value of the field based on a object which is related to the record format for the store.
16239      * @param {Object} value the value to set as. or false on reset?
16240      */
16241     setFromData : function(o){
16242         
16243         if(this.multiple){
16244             this.addItem(o);
16245             return;
16246         }
16247             
16248         var dv = ''; // display value
16249         var vv = ''; // value value..
16250         this.lastData = o;
16251         if (this.displayField) {
16252             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16253         } else {
16254             // this is an error condition!!!
16255             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16256         }
16257         
16258         if(this.valueField){
16259             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16260         }
16261         
16262         var close = this.closeTriggerEl();
16263         
16264         if(close){
16265             if(dv.length || vv * 1 > 0){
16266                 close.show() ;
16267                 this.blockFocus=true;
16268             } else {
16269                 close.hide();
16270             }             
16271         }
16272         
16273         if(this.hiddenField){
16274             this.hiddenField.dom.value = vv;
16275             
16276             this.lastSelectionText = dv;
16277             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16278             this.value = vv;
16279             return;
16280         }
16281         // no hidden field.. - we store the value in 'value', but still display
16282         // display field!!!!
16283         this.lastSelectionText = dv;
16284         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16285         this.value = vv;
16286         
16287         
16288         
16289     },
16290     // private
16291     reset : function(){
16292         // overridden so that last data is reset..
16293         
16294         if(this.multiple){
16295             this.clearItem();
16296             return;
16297         }
16298         
16299         this.setValue(this.originalValue);
16300         //this.clearInvalid();
16301         this.lastData = false;
16302         if (this.view) {
16303             this.view.clearSelections();
16304         }
16305         
16306         this.validate();
16307     },
16308     // private
16309     findRecord : function(prop, value){
16310         var record;
16311         if(this.store.getCount() > 0){
16312             this.store.each(function(r){
16313                 if(r.data[prop] == value){
16314                     record = r;
16315                     return false;
16316                 }
16317                 return true;
16318             });
16319         }
16320         return record;
16321     },
16322     
16323     getName: function()
16324     {
16325         // returns hidden if it's set..
16326         if (!this.rendered) {return ''};
16327         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16328         
16329     },
16330     // private
16331     onViewMove : function(e, t){
16332         this.inKeyMode = false;
16333     },
16334
16335     // private
16336     onViewOver : function(e, t){
16337         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16338             return;
16339         }
16340         var item = this.view.findItemFromChild(t);
16341         
16342         if(item){
16343             var index = this.view.indexOf(item);
16344             this.select(index, false);
16345         }
16346     },
16347
16348     // private
16349     onViewClick : function(view, doFocus, el, e)
16350     {
16351         var index = this.view.getSelectedIndexes()[0];
16352         
16353         var r = this.store.getAt(index);
16354         
16355         if(this.tickable){
16356             
16357             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16358                 return;
16359             }
16360             
16361             var rm = false;
16362             var _this = this;
16363             
16364             Roo.each(this.tickItems, function(v,k){
16365                 
16366                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16367                     Roo.log(v);
16368                     _this.tickItems.splice(k, 1);
16369                     
16370                     if(typeof(e) == 'undefined' && view == false){
16371                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16372                     }
16373                     
16374                     rm = true;
16375                     return;
16376                 }
16377             });
16378             
16379             if(rm){
16380                 return;
16381             }
16382             
16383             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16384                 this.tickItems.push(r.data);
16385             }
16386             
16387             if(typeof(e) == 'undefined' && view == false){
16388                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16389             }
16390                     
16391             return;
16392         }
16393         
16394         if(r){
16395             this.onSelect(r, index);
16396         }
16397         if(doFocus !== false && !this.blockFocus){
16398             this.inputEl().focus();
16399         }
16400     },
16401
16402     // private
16403     restrictHeight : function(){
16404         //this.innerList.dom.style.height = '';
16405         //var inner = this.innerList.dom;
16406         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16407         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16408         //this.list.beginUpdate();
16409         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16410         this.list.alignTo(this.inputEl(), this.listAlign);
16411         this.list.alignTo(this.inputEl(), this.listAlign);
16412         //this.list.endUpdate();
16413     },
16414
16415     // private
16416     onEmptyResults : function(){
16417         
16418         if(this.tickable && this.editable){
16419             this.hasFocus = false;
16420             this.restrictHeight();
16421             return;
16422         }
16423         
16424         this.collapse();
16425     },
16426
16427     /**
16428      * Returns true if the dropdown list is expanded, else false.
16429      */
16430     isExpanded : function(){
16431         return this.list.isVisible();
16432     },
16433
16434     /**
16435      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16436      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16437      * @param {String} value The data value of the item to select
16438      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16439      * selected item if it is not currently in view (defaults to true)
16440      * @return {Boolean} True if the value matched an item in the list, else false
16441      */
16442     selectByValue : function(v, scrollIntoView){
16443         if(v !== undefined && v !== null){
16444             var r = this.findRecord(this.valueField || this.displayField, v);
16445             if(r){
16446                 this.select(this.store.indexOf(r), scrollIntoView);
16447                 return true;
16448             }
16449         }
16450         return false;
16451     },
16452
16453     /**
16454      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16455      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16456      * @param {Number} index The zero-based index of the list item to select
16457      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16458      * selected item if it is not currently in view (defaults to true)
16459      */
16460     select : function(index, scrollIntoView){
16461         this.selectedIndex = index;
16462         this.view.select(index);
16463         if(scrollIntoView !== false){
16464             var el = this.view.getNode(index);
16465             /*
16466              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16467              */
16468             if(el){
16469                 this.list.scrollChildIntoView(el, false);
16470             }
16471         }
16472     },
16473
16474     // private
16475     selectNext : function(){
16476         var ct = this.store.getCount();
16477         if(ct > 0){
16478             if(this.selectedIndex == -1){
16479                 this.select(0);
16480             }else if(this.selectedIndex < ct-1){
16481                 this.select(this.selectedIndex+1);
16482             }
16483         }
16484     },
16485
16486     // private
16487     selectPrev : function(){
16488         var ct = this.store.getCount();
16489         if(ct > 0){
16490             if(this.selectedIndex == -1){
16491                 this.select(0);
16492             }else if(this.selectedIndex != 0){
16493                 this.select(this.selectedIndex-1);
16494             }
16495         }
16496     },
16497
16498     // private
16499     onKeyUp : function(e){
16500         if(this.editable !== false && !e.isSpecialKey()){
16501             this.lastKey = e.getKey();
16502             this.dqTask.delay(this.queryDelay);
16503         }
16504     },
16505
16506     // private
16507     validateBlur : function(){
16508         return !this.list || !this.list.isVisible();   
16509     },
16510
16511     // private
16512     initQuery : function(){
16513         
16514         var v = this.getRawValue();
16515         
16516         if(this.tickable && this.editable){
16517             v = this.tickableInputEl().getValue();
16518         }
16519         
16520         this.doQuery(v);
16521     },
16522
16523     // private
16524     doForce : function(){
16525         if(this.inputEl().dom.value.length > 0){
16526             this.inputEl().dom.value =
16527                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16528              
16529         }
16530     },
16531
16532     /**
16533      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16534      * query allowing the query action to be canceled if needed.
16535      * @param {String} query The SQL query to execute
16536      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16537      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16538      * saved in the current store (defaults to false)
16539      */
16540     doQuery : function(q, forceAll){
16541         
16542         if(q === undefined || q === null){
16543             q = '';
16544         }
16545         var qe = {
16546             query: q,
16547             forceAll: forceAll,
16548             combo: this,
16549             cancel:false
16550         };
16551         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16552             return false;
16553         }
16554         q = qe.query;
16555         
16556         forceAll = qe.forceAll;
16557         if(forceAll === true || (q.length >= this.minChars)){
16558             
16559             this.hasQuery = true;
16560             
16561             if(this.lastQuery != q || this.alwaysQuery){
16562                 this.lastQuery = q;
16563                 if(this.mode == 'local'){
16564                     this.selectedIndex = -1;
16565                     if(forceAll){
16566                         this.store.clearFilter();
16567                     }else{
16568                         
16569                         if(this.specialFilter){
16570                             this.fireEvent('specialfilter', this);
16571                             this.onLoad();
16572                             return;
16573                         }
16574                         
16575                         this.store.filter(this.displayField, q);
16576                     }
16577                     
16578                     this.store.fireEvent("datachanged", this.store);
16579                     
16580                     this.onLoad();
16581                     
16582                     
16583                 }else{
16584                     
16585                     this.store.baseParams[this.queryParam] = q;
16586                     
16587                     var options = {params : this.getParams(q)};
16588                     
16589                     if(this.loadNext){
16590                         options.add = true;
16591                         options.params.start = this.page * this.pageSize;
16592                     }
16593                     
16594                     this.store.load(options);
16595                     
16596                     /*
16597                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16598                      *  we should expand the list on onLoad
16599                      *  so command out it
16600                      */
16601 //                    this.expand();
16602                 }
16603             }else{
16604                 this.selectedIndex = -1;
16605                 this.onLoad();   
16606             }
16607         }
16608         
16609         this.loadNext = false;
16610     },
16611     
16612     // private
16613     getParams : function(q){
16614         var p = {};
16615         //p[this.queryParam] = q;
16616         
16617         if(this.pageSize){
16618             p.start = 0;
16619             p.limit = this.pageSize;
16620         }
16621         return p;
16622     },
16623
16624     /**
16625      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16626      */
16627     collapse : function(){
16628         if(!this.isExpanded()){
16629             return;
16630         }
16631         
16632         this.list.hide();
16633         
16634         this.hasFocus = false;
16635         
16636         if(this.tickable){
16637             this.okBtn.hide();
16638             this.cancelBtn.hide();
16639             this.trigger.show();
16640             
16641             if(this.editable){
16642                 this.tickableInputEl().dom.value = '';
16643                 this.tickableInputEl().blur();
16644             }
16645             
16646         }
16647         
16648         Roo.get(document).un('mousedown', this.collapseIf, this);
16649         Roo.get(document).un('mousewheel', this.collapseIf, this);
16650         if (!this.editable) {
16651             Roo.get(document).un('keydown', this.listKeyPress, this);
16652         }
16653         this.fireEvent('collapse', this);
16654         
16655         this.validate();
16656     },
16657
16658     // private
16659     collapseIf : function(e){
16660         var in_combo  = e.within(this.el);
16661         var in_list =  e.within(this.list);
16662         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16663         
16664         if (in_combo || in_list || is_list) {
16665             //e.stopPropagation();
16666             return;
16667         }
16668         
16669         if(this.tickable){
16670             this.onTickableFooterButtonClick(e, false, false);
16671         }
16672
16673         this.collapse();
16674         
16675     },
16676
16677     /**
16678      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16679      */
16680     expand : function(){
16681        
16682         if(this.isExpanded() || !this.hasFocus){
16683             return;
16684         }
16685         
16686         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16687         this.list.setWidth(lw);
16688         
16689         Roo.log('expand');
16690         
16691         this.list.show();
16692         
16693         this.restrictHeight();
16694         
16695         if(this.tickable){
16696             
16697             this.tickItems = Roo.apply([], this.item);
16698             
16699             this.okBtn.show();
16700             this.cancelBtn.show();
16701             this.trigger.hide();
16702             
16703             if(this.editable){
16704                 this.tickableInputEl().focus();
16705             }
16706             
16707         }
16708         
16709         Roo.get(document).on('mousedown', this.collapseIf, this);
16710         Roo.get(document).on('mousewheel', this.collapseIf, this);
16711         if (!this.editable) {
16712             Roo.get(document).on('keydown', this.listKeyPress, this);
16713         }
16714         
16715         this.fireEvent('expand', this);
16716     },
16717
16718     // private
16719     // Implements the default empty TriggerField.onTriggerClick function
16720     onTriggerClick : function(e)
16721     {
16722         Roo.log('trigger click');
16723         
16724         if(this.disabled || !this.triggerList){
16725             return;
16726         }
16727         
16728         this.page = 0;
16729         this.loadNext = false;
16730         
16731         if(this.isExpanded()){
16732             this.collapse();
16733             if (!this.blockFocus) {
16734                 this.inputEl().focus();
16735             }
16736             
16737         }else {
16738             this.hasFocus = true;
16739             if(this.triggerAction == 'all') {
16740                 this.doQuery(this.allQuery, true);
16741             } else {
16742                 this.doQuery(this.getRawValue());
16743             }
16744             if (!this.blockFocus) {
16745                 this.inputEl().focus();
16746             }
16747         }
16748     },
16749     
16750     onTickableTriggerClick : function(e)
16751     {
16752         if(this.disabled){
16753             return;
16754         }
16755         
16756         this.page = 0;
16757         this.loadNext = false;
16758         this.hasFocus = true;
16759         
16760         if(this.triggerAction == 'all') {
16761             this.doQuery(this.allQuery, true);
16762         } else {
16763             this.doQuery(this.getRawValue());
16764         }
16765     },
16766     
16767     onSearchFieldClick : function(e)
16768     {
16769         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16770             this.onTickableFooterButtonClick(e, false, false);
16771             return;
16772         }
16773         
16774         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16775             return;
16776         }
16777         
16778         this.page = 0;
16779         this.loadNext = false;
16780         this.hasFocus = true;
16781         
16782         if(this.triggerAction == 'all') {
16783             this.doQuery(this.allQuery, true);
16784         } else {
16785             this.doQuery(this.getRawValue());
16786         }
16787     },
16788     
16789     listKeyPress : function(e)
16790     {
16791         //Roo.log('listkeypress');
16792         // scroll to first matching element based on key pres..
16793         if (e.isSpecialKey()) {
16794             return false;
16795         }
16796         var k = String.fromCharCode(e.getKey()).toUpperCase();
16797         //Roo.log(k);
16798         var match  = false;
16799         var csel = this.view.getSelectedNodes();
16800         var cselitem = false;
16801         if (csel.length) {
16802             var ix = this.view.indexOf(csel[0]);
16803             cselitem  = this.store.getAt(ix);
16804             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16805                 cselitem = false;
16806             }
16807             
16808         }
16809         
16810         this.store.each(function(v) { 
16811             if (cselitem) {
16812                 // start at existing selection.
16813                 if (cselitem.id == v.id) {
16814                     cselitem = false;
16815                 }
16816                 return true;
16817             }
16818                 
16819             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16820                 match = this.store.indexOf(v);
16821                 return false;
16822             }
16823             return true;
16824         }, this);
16825         
16826         if (match === false) {
16827             return true; // no more action?
16828         }
16829         // scroll to?
16830         this.view.select(match);
16831         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16832         sn.scrollIntoView(sn.dom.parentNode, false);
16833     },
16834     
16835     onViewScroll : function(e, t){
16836         
16837         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){
16838             return;
16839         }
16840         
16841         this.hasQuery = true;
16842         
16843         this.loading = this.list.select('.loading', true).first();
16844         
16845         if(this.loading === null){
16846             this.list.createChild({
16847                 tag: 'div',
16848                 cls: 'loading roo-select2-more-results roo-select2-active',
16849                 html: 'Loading more results...'
16850             });
16851             
16852             this.loading = this.list.select('.loading', true).first();
16853             
16854             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16855             
16856             this.loading.hide();
16857         }
16858         
16859         this.loading.show();
16860         
16861         var _combo = this;
16862         
16863         this.page++;
16864         this.loadNext = true;
16865         
16866         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16867         
16868         return;
16869     },
16870     
16871     addItem : function(o)
16872     {   
16873         var dv = ''; // display value
16874         
16875         if (this.displayField) {
16876             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16877         } else {
16878             // this is an error condition!!!
16879             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16880         }
16881         
16882         if(!dv.length){
16883             return;
16884         }
16885         
16886         var choice = this.choices.createChild({
16887             tag: 'li',
16888             cls: 'roo-select2-search-choice',
16889             cn: [
16890                 {
16891                     tag: 'div',
16892                     html: dv
16893                 },
16894                 {
16895                     tag: 'a',
16896                     href: '#',
16897                     cls: 'roo-select2-search-choice-close fa fa-times',
16898                     tabindex: '-1'
16899                 }
16900             ]
16901             
16902         }, this.searchField);
16903         
16904         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16905         
16906         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16907         
16908         this.item.push(o);
16909         
16910         this.lastData = o;
16911         
16912         this.syncValue();
16913         
16914         this.inputEl().dom.value = '';
16915         
16916         this.validate();
16917     },
16918     
16919     onRemoveItem : function(e, _self, o)
16920     {
16921         e.preventDefault();
16922         
16923         this.lastItem = Roo.apply([], this.item);
16924         
16925         var index = this.item.indexOf(o.data) * 1;
16926         
16927         if( index < 0){
16928             Roo.log('not this item?!');
16929             return;
16930         }
16931         
16932         this.item.splice(index, 1);
16933         o.item.remove();
16934         
16935         this.syncValue();
16936         
16937         this.fireEvent('remove', this, e);
16938         
16939         this.validate();
16940         
16941     },
16942     
16943     syncValue : function()
16944     {
16945         if(!this.item.length){
16946             this.clearValue();
16947             return;
16948         }
16949             
16950         var value = [];
16951         var _this = this;
16952         Roo.each(this.item, function(i){
16953             if(_this.valueField){
16954                 value.push(i[_this.valueField]);
16955                 return;
16956             }
16957
16958             value.push(i);
16959         });
16960
16961         this.value = value.join(',');
16962
16963         if(this.hiddenField){
16964             this.hiddenField.dom.value = this.value;
16965         }
16966         
16967         this.store.fireEvent("datachanged", this.store);
16968         
16969         this.validate();
16970     },
16971     
16972     clearItem : function()
16973     {
16974         if(!this.multiple){
16975             return;
16976         }
16977         
16978         this.item = [];
16979         
16980         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16981            c.remove();
16982         });
16983         
16984         this.syncValue();
16985         
16986         this.validate();
16987         
16988         if(this.tickable && !Roo.isTouch){
16989             this.view.refresh();
16990         }
16991     },
16992     
16993     inputEl: function ()
16994     {
16995         if(Roo.isIOS && this.useNativeIOS){
16996             return this.el.select('select.roo-ios-select', true).first();
16997         }
16998         
16999         if(Roo.isTouch && this.mobileTouchView){
17000             return this.el.select('input.form-control',true).first();
17001         }
17002         
17003         if(this.tickable){
17004             return this.searchField;
17005         }
17006         
17007         return this.el.select('input.form-control',true).first();
17008     },
17009     
17010     onTickableFooterButtonClick : function(e, btn, el)
17011     {
17012         e.preventDefault();
17013         
17014         this.lastItem = Roo.apply([], this.item);
17015         
17016         if(btn && btn.name == 'cancel'){
17017             this.tickItems = Roo.apply([], this.item);
17018             this.collapse();
17019             return;
17020         }
17021         
17022         this.clearItem();
17023         
17024         var _this = this;
17025         
17026         Roo.each(this.tickItems, function(o){
17027             _this.addItem(o);
17028         });
17029         
17030         this.collapse();
17031         
17032     },
17033     
17034     validate : function()
17035     {
17036         if(this.getVisibilityEl().hasClass('hidden')){
17037             return true;
17038         }
17039         
17040         var v = this.getRawValue();
17041         
17042         if(this.multiple){
17043             v = this.getValue();
17044         }
17045         
17046         if(this.disabled || this.allowBlank || v.length){
17047             this.markValid();
17048             return true;
17049         }
17050         
17051         this.markInvalid();
17052         return false;
17053     },
17054     
17055     tickableInputEl : function()
17056     {
17057         if(!this.tickable || !this.editable){
17058             return this.inputEl();
17059         }
17060         
17061         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17062     },
17063     
17064     
17065     getAutoCreateTouchView : function()
17066     {
17067         var id = Roo.id();
17068         
17069         var cfg = {
17070             cls: 'form-group' //input-group
17071         };
17072         
17073         var input =  {
17074             tag: 'input',
17075             id : id,
17076             type : this.inputType,
17077             cls : 'form-control x-combo-noedit',
17078             autocomplete: 'new-password',
17079             placeholder : this.placeholder || '',
17080             readonly : true
17081         };
17082         
17083         if (this.name) {
17084             input.name = this.name;
17085         }
17086         
17087         if (this.size) {
17088             input.cls += ' input-' + this.size;
17089         }
17090         
17091         if (this.disabled) {
17092             input.disabled = true;
17093         }
17094         
17095         var inputblock = {
17096             cls : 'roo-combobox-wrap',
17097             cn : [
17098                 input
17099             ]
17100         };
17101         
17102         if(this.before){
17103             inputblock.cls += ' input-group';
17104             
17105             inputblock.cn.unshift({
17106                 tag :'span',
17107                 cls : 'input-group-addon input-group-prepend input-group-text',
17108                 html : this.before
17109             });
17110         }
17111         
17112         if(this.removable && !this.multiple){
17113             inputblock.cls += ' roo-removable';
17114             
17115             inputblock.cn.push({
17116                 tag: 'button',
17117                 html : 'x',
17118                 cls : 'roo-combo-removable-btn close'
17119             });
17120         }
17121
17122         if(this.hasFeedback && !this.allowBlank){
17123             
17124             inputblock.cls += ' has-feedback';
17125             
17126             inputblock.cn.push({
17127                 tag: 'span',
17128                 cls: 'glyphicon form-control-feedback'
17129             });
17130             
17131         }
17132         
17133         if (this.after) {
17134             
17135             inputblock.cls += (this.before) ? '' : ' input-group';
17136             
17137             inputblock.cn.push({
17138                 tag :'span',
17139                 cls : 'input-group-addon input-group-append input-group-text',
17140                 html : this.after
17141             });
17142         }
17143
17144         
17145         var ibwrap = inputblock;
17146         
17147         if(this.multiple){
17148             ibwrap = {
17149                 tag: 'ul',
17150                 cls: 'roo-select2-choices',
17151                 cn:[
17152                     {
17153                         tag: 'li',
17154                         cls: 'roo-select2-search-field',
17155                         cn: [
17156
17157                             inputblock
17158                         ]
17159                     }
17160                 ]
17161             };
17162         
17163             
17164         }
17165         
17166         var combobox = {
17167             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17168             cn: [
17169                 {
17170                     tag: 'input',
17171                     type : 'hidden',
17172                     cls: 'form-hidden-field'
17173                 },
17174                 ibwrap
17175             ]
17176         };
17177         
17178         if(!this.multiple && this.showToggleBtn){
17179             
17180             var caret = {
17181                 cls: 'caret'
17182             };
17183             
17184             if (this.caret != false) {
17185                 caret = {
17186                      tag: 'i',
17187                      cls: 'fa fa-' + this.caret
17188                 };
17189                 
17190             }
17191             
17192             combobox.cn.push({
17193                 tag :'span',
17194                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17195                 cn : [
17196                     Roo.bootstrap.version == 3 ? caret : '',
17197                     {
17198                         tag: 'span',
17199                         cls: 'combobox-clear',
17200                         cn  : [
17201                             {
17202                                 tag : 'i',
17203                                 cls: 'icon-remove'
17204                             }
17205                         ]
17206                     }
17207                 ]
17208
17209             })
17210         }
17211         
17212         if(this.multiple){
17213             combobox.cls += ' roo-select2-container-multi';
17214         }
17215         
17216         var align = this.labelAlign || this.parentLabelAlign();
17217         
17218         if (align ==='left' && this.fieldLabel.length) {
17219
17220             cfg.cn = [
17221                 {
17222                    tag : 'i',
17223                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17224                    tooltip : 'This field is required'
17225                 },
17226                 {
17227                     tag: 'label',
17228                     cls : 'control-label col-form-label',
17229                     html : this.fieldLabel
17230
17231                 },
17232                 {
17233                     cls : 'roo-combobox-wrap ', 
17234                     cn: [
17235                         combobox
17236                     ]
17237                 }
17238             ];
17239             
17240             var labelCfg = cfg.cn[1];
17241             var contentCfg = cfg.cn[2];
17242             
17243
17244             if(this.indicatorpos == 'right'){
17245                 cfg.cn = [
17246                     {
17247                         tag: 'label',
17248                         'for' :  id,
17249                         cls : 'control-label col-form-label',
17250                         cn : [
17251                             {
17252                                 tag : 'span',
17253                                 html : this.fieldLabel
17254                             },
17255                             {
17256                                 tag : 'i',
17257                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17258                                 tooltip : 'This field is required'
17259                             }
17260                         ]
17261                     },
17262                     {
17263                         cls : "roo-combobox-wrap ",
17264                         cn: [
17265                             combobox
17266                         ]
17267                     }
17268
17269                 ];
17270                 
17271                 labelCfg = cfg.cn[0];
17272                 contentCfg = cfg.cn[1];
17273             }
17274             
17275            
17276             
17277             if(this.labelWidth > 12){
17278                 labelCfg.style = "width: " + this.labelWidth + 'px';
17279             }
17280            
17281             if(this.labelWidth < 13 && this.labelmd == 0){
17282                 this.labelmd = this.labelWidth;
17283             }
17284             
17285             if(this.labellg > 0){
17286                 labelCfg.cls += ' col-lg-' + this.labellg;
17287                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17288             }
17289             
17290             if(this.labelmd > 0){
17291                 labelCfg.cls += ' col-md-' + this.labelmd;
17292                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17293             }
17294             
17295             if(this.labelsm > 0){
17296                 labelCfg.cls += ' col-sm-' + this.labelsm;
17297                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17298             }
17299             
17300             if(this.labelxs > 0){
17301                 labelCfg.cls += ' col-xs-' + this.labelxs;
17302                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17303             }
17304                 
17305                 
17306         } else if ( this.fieldLabel.length) {
17307             cfg.cn = [
17308                 {
17309                    tag : 'i',
17310                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17311                    tooltip : 'This field is required'
17312                 },
17313                 {
17314                     tag: 'label',
17315                     cls : 'control-label',
17316                     html : this.fieldLabel
17317
17318                 },
17319                 {
17320                     cls : '', 
17321                     cn: [
17322                         combobox
17323                     ]
17324                 }
17325             ];
17326             
17327             if(this.indicatorpos == 'right'){
17328                 cfg.cn = [
17329                     {
17330                         tag: 'label',
17331                         cls : 'control-label',
17332                         html : this.fieldLabel,
17333                         cn : [
17334                             {
17335                                tag : 'i',
17336                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17337                                tooltip : 'This field is required'
17338                             }
17339                         ]
17340                     },
17341                     {
17342                         cls : '', 
17343                         cn: [
17344                             combobox
17345                         ]
17346                     }
17347                 ];
17348             }
17349         } else {
17350             cfg.cn = combobox;    
17351         }
17352         
17353         
17354         var settings = this;
17355         
17356         ['xs','sm','md','lg'].map(function(size){
17357             if (settings[size]) {
17358                 cfg.cls += ' col-' + size + '-' + settings[size];
17359             }
17360         });
17361         
17362         return cfg;
17363     },
17364     
17365     initTouchView : function()
17366     {
17367         this.renderTouchView();
17368         
17369         this.touchViewEl.on('scroll', function(){
17370             this.el.dom.scrollTop = 0;
17371         }, this);
17372         
17373         this.originalValue = this.getValue();
17374         
17375         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17376         
17377         this.inputEl().on("click", this.showTouchView, this);
17378         if (this.triggerEl) {
17379             this.triggerEl.on("click", this.showTouchView, this);
17380         }
17381         
17382         
17383         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17384         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17385         
17386         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17387         
17388         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17389         this.store.on('load', this.onTouchViewLoad, this);
17390         this.store.on('loadexception', this.onTouchViewLoadException, this);
17391         
17392         if(this.hiddenName){
17393             
17394             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17395             
17396             this.hiddenField.dom.value =
17397                 this.hiddenValue !== undefined ? this.hiddenValue :
17398                 this.value !== undefined ? this.value : '';
17399         
17400             this.el.dom.removeAttribute('name');
17401             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17402         }
17403         
17404         if(this.multiple){
17405             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17406             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17407         }
17408         
17409         if(this.removable && !this.multiple){
17410             var close = this.closeTriggerEl();
17411             if(close){
17412                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17413                 close.on('click', this.removeBtnClick, this, close);
17414             }
17415         }
17416         /*
17417          * fix the bug in Safari iOS8
17418          */
17419         this.inputEl().on("focus", function(e){
17420             document.activeElement.blur();
17421         }, this);
17422         
17423         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17424         
17425         return;
17426         
17427         
17428     },
17429     
17430     renderTouchView : function()
17431     {
17432         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17433         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17436         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         
17438         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17439         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17440         this.touchViewBodyEl.setStyle('overflow', 'auto');
17441         
17442         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17443         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17446         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17447         
17448     },
17449     
17450     showTouchView : function()
17451     {
17452         if(this.disabled){
17453             return;
17454         }
17455         
17456         this.touchViewHeaderEl.hide();
17457
17458         if(this.modalTitle.length){
17459             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17460             this.touchViewHeaderEl.show();
17461         }
17462
17463         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17464         this.touchViewEl.show();
17465
17466         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17467         
17468         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17469         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17470
17471         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17472
17473         if(this.modalTitle.length){
17474             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17475         }
17476         
17477         this.touchViewBodyEl.setHeight(bodyHeight);
17478
17479         if(this.animate){
17480             var _this = this;
17481             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17482         }else{
17483             this.touchViewEl.addClass(['in','show']);
17484         }
17485         
17486         if(this._touchViewMask){
17487             Roo.get(document.body).addClass("x-body-masked");
17488             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17489             this._touchViewMask.setStyle('z-index', 10000);
17490             this._touchViewMask.addClass('show');
17491         }
17492         
17493         this.doTouchViewQuery();
17494         
17495     },
17496     
17497     hideTouchView : function()
17498     {
17499         this.touchViewEl.removeClass(['in','show']);
17500
17501         if(this.animate){
17502             var _this = this;
17503             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17504         }else{
17505             this.touchViewEl.setStyle('display', 'none');
17506         }
17507         
17508         if(this._touchViewMask){
17509             this._touchViewMask.removeClass('show');
17510             Roo.get(document.body).removeClass("x-body-masked");
17511         }
17512     },
17513     
17514     setTouchViewValue : function()
17515     {
17516         if(this.multiple){
17517             this.clearItem();
17518         
17519             var _this = this;
17520
17521             Roo.each(this.tickItems, function(o){
17522                 this.addItem(o);
17523             }, this);
17524         }
17525         
17526         this.hideTouchView();
17527     },
17528     
17529     doTouchViewQuery : function()
17530     {
17531         var qe = {
17532             query: '',
17533             forceAll: true,
17534             combo: this,
17535             cancel:false
17536         };
17537         
17538         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17539             return false;
17540         }
17541         
17542         if(!this.alwaysQuery || this.mode == 'local'){
17543             this.onTouchViewLoad();
17544             return;
17545         }
17546         
17547         this.store.load();
17548     },
17549     
17550     onTouchViewBeforeLoad : function(combo,opts)
17551     {
17552         return;
17553     },
17554
17555     // private
17556     onTouchViewLoad : function()
17557     {
17558         if(this.store.getCount() < 1){
17559             this.onTouchViewEmptyResults();
17560             return;
17561         }
17562         
17563         this.clearTouchView();
17564         
17565         var rawValue = this.getRawValue();
17566         
17567         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17568         
17569         this.tickItems = [];
17570         
17571         this.store.data.each(function(d, rowIndex){
17572             var row = this.touchViewListGroup.createChild(template);
17573             
17574             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17575                 row.addClass(d.data.cls);
17576             }
17577             
17578             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17579                 var cfg = {
17580                     data : d.data,
17581                     html : d.data[this.displayField]
17582                 };
17583                 
17584                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17585                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17586                 }
17587             }
17588             row.removeClass('selected');
17589             if(!this.multiple && this.valueField &&
17590                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17591             {
17592                 // radio buttons..
17593                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17594                 row.addClass('selected');
17595             }
17596             
17597             if(this.multiple && this.valueField &&
17598                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17599             {
17600                 
17601                 // checkboxes...
17602                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17603                 this.tickItems.push(d.data);
17604             }
17605             
17606             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17607             
17608         }, this);
17609         
17610         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17611         
17612         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17613
17614         if(this.modalTitle.length){
17615             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17616         }
17617
17618         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17619         
17620         if(this.mobile_restrict_height && listHeight < bodyHeight){
17621             this.touchViewBodyEl.setHeight(listHeight);
17622         }
17623         
17624         var _this = this;
17625         
17626         if(firstChecked && listHeight > bodyHeight){
17627             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17628         }
17629         
17630     },
17631     
17632     onTouchViewLoadException : function()
17633     {
17634         this.hideTouchView();
17635     },
17636     
17637     onTouchViewEmptyResults : function()
17638     {
17639         this.clearTouchView();
17640         
17641         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17642         
17643         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17644         
17645     },
17646     
17647     clearTouchView : function()
17648     {
17649         this.touchViewListGroup.dom.innerHTML = '';
17650     },
17651     
17652     onTouchViewClick : function(e, el, o)
17653     {
17654         e.preventDefault();
17655         
17656         var row = o.row;
17657         var rowIndex = o.rowIndex;
17658         
17659         var r = this.store.getAt(rowIndex);
17660         
17661         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17662             
17663             if(!this.multiple){
17664                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17665                     c.dom.removeAttribute('checked');
17666                 }, this);
17667
17668                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17669
17670                 this.setFromData(r.data);
17671
17672                 var close = this.closeTriggerEl();
17673
17674                 if(close){
17675                     close.show();
17676                 }
17677
17678                 this.hideTouchView();
17679
17680                 this.fireEvent('select', this, r, rowIndex);
17681
17682                 return;
17683             }
17684
17685             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17686                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17687                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17688                 return;
17689             }
17690
17691             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17692             this.addItem(r.data);
17693             this.tickItems.push(r.data);
17694         }
17695     },
17696     
17697     getAutoCreateNativeIOS : function()
17698     {
17699         var cfg = {
17700             cls: 'form-group' //input-group,
17701         };
17702         
17703         var combobox =  {
17704             tag: 'select',
17705             cls : 'roo-ios-select'
17706         };
17707         
17708         if (this.name) {
17709             combobox.name = this.name;
17710         }
17711         
17712         if (this.disabled) {
17713             combobox.disabled = true;
17714         }
17715         
17716         var settings = this;
17717         
17718         ['xs','sm','md','lg'].map(function(size){
17719             if (settings[size]) {
17720                 cfg.cls += ' col-' + size + '-' + settings[size];
17721             }
17722         });
17723         
17724         cfg.cn = combobox;
17725         
17726         return cfg;
17727         
17728     },
17729     
17730     initIOSView : function()
17731     {
17732         this.store.on('load', this.onIOSViewLoad, this);
17733         
17734         return;
17735     },
17736     
17737     onIOSViewLoad : function()
17738     {
17739         if(this.store.getCount() < 1){
17740             return;
17741         }
17742         
17743         this.clearIOSView();
17744         
17745         if(this.allowBlank) {
17746             
17747             var default_text = '-- SELECT --';
17748             
17749             if(this.placeholder.length){
17750                 default_text = this.placeholder;
17751             }
17752             
17753             if(this.emptyTitle.length){
17754                 default_text += ' - ' + this.emptyTitle + ' -';
17755             }
17756             
17757             var opt = this.inputEl().createChild({
17758                 tag: 'option',
17759                 value : 0,
17760                 html : default_text
17761             });
17762             
17763             var o = {};
17764             o[this.valueField] = 0;
17765             o[this.displayField] = default_text;
17766             
17767             this.ios_options.push({
17768                 data : o,
17769                 el : opt
17770             });
17771             
17772         }
17773         
17774         this.store.data.each(function(d, rowIndex){
17775             
17776             var html = '';
17777             
17778             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17779                 html = d.data[this.displayField];
17780             }
17781             
17782             var value = '';
17783             
17784             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17785                 value = d.data[this.valueField];
17786             }
17787             
17788             var option = {
17789                 tag: 'option',
17790                 value : value,
17791                 html : html
17792             };
17793             
17794             if(this.value == d.data[this.valueField]){
17795                 option['selected'] = true;
17796             }
17797             
17798             var opt = this.inputEl().createChild(option);
17799             
17800             this.ios_options.push({
17801                 data : d.data,
17802                 el : opt
17803             });
17804             
17805         }, this);
17806         
17807         this.inputEl().on('change', function(){
17808            this.fireEvent('select', this);
17809         }, this);
17810         
17811     },
17812     
17813     clearIOSView: function()
17814     {
17815         this.inputEl().dom.innerHTML = '';
17816         
17817         this.ios_options = [];
17818     },
17819     
17820     setIOSValue: function(v)
17821     {
17822         this.value = v;
17823         
17824         if(!this.ios_options){
17825             return;
17826         }
17827         
17828         Roo.each(this.ios_options, function(opts){
17829            
17830            opts.el.dom.removeAttribute('selected');
17831            
17832            if(opts.data[this.valueField] != v){
17833                return;
17834            }
17835            
17836            opts.el.dom.setAttribute('selected', true);
17837            
17838         }, this);
17839     }
17840
17841     /** 
17842     * @cfg {Boolean} grow 
17843     * @hide 
17844     */
17845     /** 
17846     * @cfg {Number} growMin 
17847     * @hide 
17848     */
17849     /** 
17850     * @cfg {Number} growMax 
17851     * @hide 
17852     */
17853     /**
17854      * @hide
17855      * @method autoSize
17856      */
17857 });
17858
17859 Roo.apply(Roo.bootstrap.ComboBox,  {
17860     
17861     header : {
17862         tag: 'div',
17863         cls: 'modal-header',
17864         cn: [
17865             {
17866                 tag: 'h4',
17867                 cls: 'modal-title'
17868             }
17869         ]
17870     },
17871     
17872     body : {
17873         tag: 'div',
17874         cls: 'modal-body',
17875         cn: [
17876             {
17877                 tag: 'ul',
17878                 cls: 'list-group'
17879             }
17880         ]
17881     },
17882     
17883     listItemRadio : {
17884         tag: 'li',
17885         cls: 'list-group-item',
17886         cn: [
17887             {
17888                 tag: 'span',
17889                 cls: 'roo-combobox-list-group-item-value'
17890             },
17891             {
17892                 tag: 'div',
17893                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17894                 cn: [
17895                     {
17896                         tag: 'input',
17897                         type: 'radio'
17898                     },
17899                     {
17900                         tag: 'label'
17901                     }
17902                 ]
17903             }
17904         ]
17905     },
17906     
17907     listItemCheckbox : {
17908         tag: 'li',
17909         cls: 'list-group-item',
17910         cn: [
17911             {
17912                 tag: 'span',
17913                 cls: 'roo-combobox-list-group-item-value'
17914             },
17915             {
17916                 tag: 'div',
17917                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17918                 cn: [
17919                     {
17920                         tag: 'input',
17921                         type: 'checkbox'
17922                     },
17923                     {
17924                         tag: 'label'
17925                     }
17926                 ]
17927             }
17928         ]
17929     },
17930     
17931     emptyResult : {
17932         tag: 'div',
17933         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17934     },
17935     
17936     footer : {
17937         tag: 'div',
17938         cls: 'modal-footer',
17939         cn: [
17940             {
17941                 tag: 'div',
17942                 cls: 'row',
17943                 cn: [
17944                     {
17945                         tag: 'div',
17946                         cls: 'col-xs-6 text-left',
17947                         cn: {
17948                             tag: 'button',
17949                             cls: 'btn btn-danger roo-touch-view-cancel',
17950                             html: 'Cancel'
17951                         }
17952                     },
17953                     {
17954                         tag: 'div',
17955                         cls: 'col-xs-6 text-right',
17956                         cn: {
17957                             tag: 'button',
17958                             cls: 'btn btn-success roo-touch-view-ok',
17959                             html: 'OK'
17960                         }
17961                     }
17962                 ]
17963             }
17964         ]
17965         
17966     }
17967 });
17968
17969 Roo.apply(Roo.bootstrap.ComboBox,  {
17970     
17971     touchViewTemplate : {
17972         tag: 'div',
17973         cls: 'modal fade roo-combobox-touch-view',
17974         cn: [
17975             {
17976                 tag: 'div',
17977                 cls: 'modal-dialog',
17978                 style : 'position:fixed', // we have to fix position....
17979                 cn: [
17980                     {
17981                         tag: 'div',
17982                         cls: 'modal-content',
17983                         cn: [
17984                             Roo.bootstrap.ComboBox.header,
17985                             Roo.bootstrap.ComboBox.body,
17986                             Roo.bootstrap.ComboBox.footer
17987                         ]
17988                     }
17989                 ]
17990             }
17991         ]
17992     }
17993 });/*
17994  * Based on:
17995  * Ext JS Library 1.1.1
17996  * Copyright(c) 2006-2007, Ext JS, LLC.
17997  *
17998  * Originally Released Under LGPL - original licence link has changed is not relivant.
17999  *
18000  * Fork - LGPL
18001  * <script type="text/javascript">
18002  */
18003
18004 /**
18005  * @class Roo.View
18006  * @extends Roo.util.Observable
18007  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18008  * This class also supports single and multi selection modes. <br>
18009  * Create a data model bound view:
18010  <pre><code>
18011  var store = new Roo.data.Store(...);
18012
18013  var view = new Roo.View({
18014     el : "my-element",
18015     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18016  
18017     singleSelect: true,
18018     selectedClass: "ydataview-selected",
18019     store: store
18020  });
18021
18022  // listen for node click?
18023  view.on("click", function(vw, index, node, e){
18024  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18025  });
18026
18027  // load XML data
18028  dataModel.load("foobar.xml");
18029  </code></pre>
18030  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18031  * <br><br>
18032  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18033  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18034  * 
18035  * Note: old style constructor is still suported (container, template, config)
18036  * 
18037  * @constructor
18038  * Create a new View
18039  * @param {Object} config The config object
18040  * 
18041  */
18042 Roo.View = function(config, depreciated_tpl, depreciated_config){
18043     
18044     this.parent = false;
18045     
18046     if (typeof(depreciated_tpl) == 'undefined') {
18047         // new way.. - universal constructor.
18048         Roo.apply(this, config);
18049         this.el  = Roo.get(this.el);
18050     } else {
18051         // old format..
18052         this.el  = Roo.get(config);
18053         this.tpl = depreciated_tpl;
18054         Roo.apply(this, depreciated_config);
18055     }
18056     this.wrapEl  = this.el.wrap().wrap();
18057     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18058     
18059     
18060     if(typeof(this.tpl) == "string"){
18061         this.tpl = new Roo.Template(this.tpl);
18062     } else {
18063         // support xtype ctors..
18064         this.tpl = new Roo.factory(this.tpl, Roo);
18065     }
18066     
18067     
18068     this.tpl.compile();
18069     
18070     /** @private */
18071     this.addEvents({
18072         /**
18073          * @event beforeclick
18074          * Fires before a click is processed. Returns false to cancel the default action.
18075          * @param {Roo.View} this
18076          * @param {Number} index The index of the target node
18077          * @param {HTMLElement} node The target node
18078          * @param {Roo.EventObject} e The raw event object
18079          */
18080             "beforeclick" : true,
18081         /**
18082          * @event click
18083          * Fires when a template node is clicked.
18084          * @param {Roo.View} this
18085          * @param {Number} index The index of the target node
18086          * @param {HTMLElement} node The target node
18087          * @param {Roo.EventObject} e The raw event object
18088          */
18089             "click" : true,
18090         /**
18091          * @event dblclick
18092          * Fires when a template node is double clicked.
18093          * @param {Roo.View} this
18094          * @param {Number} index The index of the target node
18095          * @param {HTMLElement} node The target node
18096          * @param {Roo.EventObject} e The raw event object
18097          */
18098             "dblclick" : true,
18099         /**
18100          * @event contextmenu
18101          * Fires when a template node is right clicked.
18102          * @param {Roo.View} this
18103          * @param {Number} index The index of the target node
18104          * @param {HTMLElement} node The target node
18105          * @param {Roo.EventObject} e The raw event object
18106          */
18107             "contextmenu" : true,
18108         /**
18109          * @event selectionchange
18110          * Fires when the selected nodes change.
18111          * @param {Roo.View} this
18112          * @param {Array} selections Array of the selected nodes
18113          */
18114             "selectionchange" : true,
18115     
18116         /**
18117          * @event beforeselect
18118          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18119          * @param {Roo.View} this
18120          * @param {HTMLElement} node The node to be selected
18121          * @param {Array} selections Array of currently selected nodes
18122          */
18123             "beforeselect" : true,
18124         /**
18125          * @event preparedata
18126          * Fires on every row to render, to allow you to change the data.
18127          * @param {Roo.View} this
18128          * @param {Object} data to be rendered (change this)
18129          */
18130           "preparedata" : true
18131           
18132           
18133         });
18134
18135
18136
18137     this.el.on({
18138         "click": this.onClick,
18139         "dblclick": this.onDblClick,
18140         "contextmenu": this.onContextMenu,
18141         scope:this
18142     });
18143
18144     this.selections = [];
18145     this.nodes = [];
18146     this.cmp = new Roo.CompositeElementLite([]);
18147     if(this.store){
18148         this.store = Roo.factory(this.store, Roo.data);
18149         this.setStore(this.store, true);
18150     }
18151     
18152     if ( this.footer && this.footer.xtype) {
18153            
18154          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18155         
18156         this.footer.dataSource = this.store;
18157         this.footer.container = fctr;
18158         this.footer = Roo.factory(this.footer, Roo);
18159         fctr.insertFirst(this.el);
18160         
18161         // this is a bit insane - as the paging toolbar seems to detach the el..
18162 //        dom.parentNode.parentNode.parentNode
18163          // they get detached?
18164     }
18165     
18166     
18167     Roo.View.superclass.constructor.call(this);
18168     
18169     
18170 };
18171
18172 Roo.extend(Roo.View, Roo.util.Observable, {
18173     
18174      /**
18175      * @cfg {Roo.data.Store} store Data store to load data from.
18176      */
18177     store : false,
18178     
18179     /**
18180      * @cfg {String|Roo.Element} el The container element.
18181      */
18182     el : '',
18183     
18184     /**
18185      * @cfg {String|Roo.Template} tpl The template used by this View 
18186      */
18187     tpl : false,
18188     /**
18189      * @cfg {String} dataName the named area of the template to use as the data area
18190      *                          Works with domtemplates roo-name="name"
18191      */
18192     dataName: false,
18193     /**
18194      * @cfg {String} selectedClass The css class to add to selected nodes
18195      */
18196     selectedClass : "x-view-selected",
18197      /**
18198      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18199      */
18200     emptyText : "",
18201     
18202     /**
18203      * @cfg {String} text to display on mask (default Loading)
18204      */
18205     mask : false,
18206     /**
18207      * @cfg {Boolean} multiSelect Allow multiple selection
18208      */
18209     multiSelect : false,
18210     /**
18211      * @cfg {Boolean} singleSelect Allow single selection
18212      */
18213     singleSelect:  false,
18214     
18215     /**
18216      * @cfg {Boolean} toggleSelect - selecting 
18217      */
18218     toggleSelect : false,
18219     
18220     /**
18221      * @cfg {Boolean} tickable - selecting 
18222      */
18223     tickable : false,
18224     
18225     /**
18226      * Returns the element this view is bound to.
18227      * @return {Roo.Element}
18228      */
18229     getEl : function(){
18230         return this.wrapEl;
18231     },
18232     
18233     
18234
18235     /**
18236      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18237      */
18238     refresh : function(){
18239         //Roo.log('refresh');
18240         var t = this.tpl;
18241         
18242         // if we are using something like 'domtemplate', then
18243         // the what gets used is:
18244         // t.applySubtemplate(NAME, data, wrapping data..)
18245         // the outer template then get' applied with
18246         //     the store 'extra data'
18247         // and the body get's added to the
18248         //      roo-name="data" node?
18249         //      <span class='roo-tpl-{name}'></span> ?????
18250         
18251         
18252         
18253         this.clearSelections();
18254         this.el.update("");
18255         var html = [];
18256         var records = this.store.getRange();
18257         if(records.length < 1) {
18258             
18259             // is this valid??  = should it render a template??
18260             
18261             this.el.update(this.emptyText);
18262             return;
18263         }
18264         var el = this.el;
18265         if (this.dataName) {
18266             this.el.update(t.apply(this.store.meta)); //????
18267             el = this.el.child('.roo-tpl-' + this.dataName);
18268         }
18269         
18270         for(var i = 0, len = records.length; i < len; i++){
18271             var data = this.prepareData(records[i].data, i, records[i]);
18272             this.fireEvent("preparedata", this, data, i, records[i]);
18273             
18274             var d = Roo.apply({}, data);
18275             
18276             if(this.tickable){
18277                 Roo.apply(d, {'roo-id' : Roo.id()});
18278                 
18279                 var _this = this;
18280             
18281                 Roo.each(this.parent.item, function(item){
18282                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18283                         return;
18284                     }
18285                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18286                 });
18287             }
18288             
18289             html[html.length] = Roo.util.Format.trim(
18290                 this.dataName ?
18291                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18292                     t.apply(d)
18293             );
18294         }
18295         
18296         
18297         
18298         el.update(html.join(""));
18299         this.nodes = el.dom.childNodes;
18300         this.updateIndexes(0);
18301     },
18302     
18303
18304     /**
18305      * Function to override to reformat the data that is sent to
18306      * the template for each node.
18307      * DEPRICATED - use the preparedata event handler.
18308      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18309      * a JSON object for an UpdateManager bound view).
18310      */
18311     prepareData : function(data, index, record)
18312     {
18313         this.fireEvent("preparedata", this, data, index, record);
18314         return data;
18315     },
18316
18317     onUpdate : function(ds, record){
18318         // Roo.log('on update');   
18319         this.clearSelections();
18320         var index = this.store.indexOf(record);
18321         var n = this.nodes[index];
18322         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18323         n.parentNode.removeChild(n);
18324         this.updateIndexes(index, index);
18325     },
18326
18327     
18328     
18329 // --------- FIXME     
18330     onAdd : function(ds, records, index)
18331     {
18332         //Roo.log(['on Add', ds, records, index] );        
18333         this.clearSelections();
18334         if(this.nodes.length == 0){
18335             this.refresh();
18336             return;
18337         }
18338         var n = this.nodes[index];
18339         for(var i = 0, len = records.length; i < len; i++){
18340             var d = this.prepareData(records[i].data, i, records[i]);
18341             if(n){
18342                 this.tpl.insertBefore(n, d);
18343             }else{
18344                 
18345                 this.tpl.append(this.el, d);
18346             }
18347         }
18348         this.updateIndexes(index);
18349     },
18350
18351     onRemove : function(ds, record, index){
18352        // Roo.log('onRemove');
18353         this.clearSelections();
18354         var el = this.dataName  ?
18355             this.el.child('.roo-tpl-' + this.dataName) :
18356             this.el; 
18357         
18358         el.dom.removeChild(this.nodes[index]);
18359         this.updateIndexes(index);
18360     },
18361
18362     /**
18363      * Refresh an individual node.
18364      * @param {Number} index
18365      */
18366     refreshNode : function(index){
18367         this.onUpdate(this.store, this.store.getAt(index));
18368     },
18369
18370     updateIndexes : function(startIndex, endIndex){
18371         var ns = this.nodes;
18372         startIndex = startIndex || 0;
18373         endIndex = endIndex || ns.length - 1;
18374         for(var i = startIndex; i <= endIndex; i++){
18375             ns[i].nodeIndex = i;
18376         }
18377     },
18378
18379     /**
18380      * Changes the data store this view uses and refresh the view.
18381      * @param {Store} store
18382      */
18383     setStore : function(store, initial){
18384         if(!initial && this.store){
18385             this.store.un("datachanged", this.refresh);
18386             this.store.un("add", this.onAdd);
18387             this.store.un("remove", this.onRemove);
18388             this.store.un("update", this.onUpdate);
18389             this.store.un("clear", this.refresh);
18390             this.store.un("beforeload", this.onBeforeLoad);
18391             this.store.un("load", this.onLoad);
18392             this.store.un("loadexception", this.onLoad);
18393         }
18394         if(store){
18395           
18396             store.on("datachanged", this.refresh, this);
18397             store.on("add", this.onAdd, this);
18398             store.on("remove", this.onRemove, this);
18399             store.on("update", this.onUpdate, this);
18400             store.on("clear", this.refresh, this);
18401             store.on("beforeload", this.onBeforeLoad, this);
18402             store.on("load", this.onLoad, this);
18403             store.on("loadexception", this.onLoad, this);
18404         }
18405         
18406         if(store){
18407             this.refresh();
18408         }
18409     },
18410     /**
18411      * onbeforeLoad - masks the loading area.
18412      *
18413      */
18414     onBeforeLoad : function(store,opts)
18415     {
18416          //Roo.log('onBeforeLoad');   
18417         if (!opts.add) {
18418             this.el.update("");
18419         }
18420         this.el.mask(this.mask ? this.mask : "Loading" ); 
18421     },
18422     onLoad : function ()
18423     {
18424         this.el.unmask();
18425     },
18426     
18427
18428     /**
18429      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18430      * @param {HTMLElement} node
18431      * @return {HTMLElement} The template node
18432      */
18433     findItemFromChild : function(node){
18434         var el = this.dataName  ?
18435             this.el.child('.roo-tpl-' + this.dataName,true) :
18436             this.el.dom; 
18437         
18438         if(!node || node.parentNode == el){
18439                     return node;
18440             }
18441             var p = node.parentNode;
18442             while(p && p != el){
18443             if(p.parentNode == el){
18444                 return p;
18445             }
18446             p = p.parentNode;
18447         }
18448             return null;
18449     },
18450
18451     /** @ignore */
18452     onClick : function(e){
18453         var item = this.findItemFromChild(e.getTarget());
18454         if(item){
18455             var index = this.indexOf(item);
18456             if(this.onItemClick(item, index, e) !== false){
18457                 this.fireEvent("click", this, index, item, e);
18458             }
18459         }else{
18460             this.clearSelections();
18461         }
18462     },
18463
18464     /** @ignore */
18465     onContextMenu : function(e){
18466         var item = this.findItemFromChild(e.getTarget());
18467         if(item){
18468             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18469         }
18470     },
18471
18472     /** @ignore */
18473     onDblClick : function(e){
18474         var item = this.findItemFromChild(e.getTarget());
18475         if(item){
18476             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18477         }
18478     },
18479
18480     onItemClick : function(item, index, e)
18481     {
18482         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18483             return false;
18484         }
18485         if (this.toggleSelect) {
18486             var m = this.isSelected(item) ? 'unselect' : 'select';
18487             //Roo.log(m);
18488             var _t = this;
18489             _t[m](item, true, false);
18490             return true;
18491         }
18492         if(this.multiSelect || this.singleSelect){
18493             if(this.multiSelect && e.shiftKey && this.lastSelection){
18494                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18495             }else{
18496                 this.select(item, this.multiSelect && e.ctrlKey);
18497                 this.lastSelection = item;
18498             }
18499             
18500             if(!this.tickable){
18501                 e.preventDefault();
18502             }
18503             
18504         }
18505         return true;
18506     },
18507
18508     /**
18509      * Get the number of selected nodes.
18510      * @return {Number}
18511      */
18512     getSelectionCount : function(){
18513         return this.selections.length;
18514     },
18515
18516     /**
18517      * Get the currently selected nodes.
18518      * @return {Array} An array of HTMLElements
18519      */
18520     getSelectedNodes : function(){
18521         return this.selections;
18522     },
18523
18524     /**
18525      * Get the indexes of the selected nodes.
18526      * @return {Array}
18527      */
18528     getSelectedIndexes : function(){
18529         var indexes = [], s = this.selections;
18530         for(var i = 0, len = s.length; i < len; i++){
18531             indexes.push(s[i].nodeIndex);
18532         }
18533         return indexes;
18534     },
18535
18536     /**
18537      * Clear all selections
18538      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18539      */
18540     clearSelections : function(suppressEvent){
18541         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18542             this.cmp.elements = this.selections;
18543             this.cmp.removeClass(this.selectedClass);
18544             this.selections = [];
18545             if(!suppressEvent){
18546                 this.fireEvent("selectionchange", this, this.selections);
18547             }
18548         }
18549     },
18550
18551     /**
18552      * Returns true if the passed node is selected
18553      * @param {HTMLElement/Number} node The node or node index
18554      * @return {Boolean}
18555      */
18556     isSelected : function(node){
18557         var s = this.selections;
18558         if(s.length < 1){
18559             return false;
18560         }
18561         node = this.getNode(node);
18562         return s.indexOf(node) !== -1;
18563     },
18564
18565     /**
18566      * Selects nodes.
18567      * @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
18568      * @param {Boolean} keepExisting (optional) true to keep existing selections
18569      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18570      */
18571     select : function(nodeInfo, keepExisting, suppressEvent){
18572         if(nodeInfo instanceof Array){
18573             if(!keepExisting){
18574                 this.clearSelections(true);
18575             }
18576             for(var i = 0, len = nodeInfo.length; i < len; i++){
18577                 this.select(nodeInfo[i], true, true);
18578             }
18579             return;
18580         } 
18581         var node = this.getNode(nodeInfo);
18582         if(!node || this.isSelected(node)){
18583             return; // already selected.
18584         }
18585         if(!keepExisting){
18586             this.clearSelections(true);
18587         }
18588         
18589         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18590             Roo.fly(node).addClass(this.selectedClass);
18591             this.selections.push(node);
18592             if(!suppressEvent){
18593                 this.fireEvent("selectionchange", this, this.selections);
18594             }
18595         }
18596         
18597         
18598     },
18599       /**
18600      * Unselects nodes.
18601      * @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
18602      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18603      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18604      */
18605     unselect : function(nodeInfo, keepExisting, suppressEvent)
18606     {
18607         if(nodeInfo instanceof Array){
18608             Roo.each(this.selections, function(s) {
18609                 this.unselect(s, nodeInfo);
18610             }, this);
18611             return;
18612         }
18613         var node = this.getNode(nodeInfo);
18614         if(!node || !this.isSelected(node)){
18615             //Roo.log("not selected");
18616             return; // not selected.
18617         }
18618         // fireevent???
18619         var ns = [];
18620         Roo.each(this.selections, function(s) {
18621             if (s == node ) {
18622                 Roo.fly(node).removeClass(this.selectedClass);
18623
18624                 return;
18625             }
18626             ns.push(s);
18627         },this);
18628         
18629         this.selections= ns;
18630         this.fireEvent("selectionchange", this, this.selections);
18631     },
18632
18633     /**
18634      * Gets a template node.
18635      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18636      * @return {HTMLElement} The node or null if it wasn't found
18637      */
18638     getNode : function(nodeInfo){
18639         if(typeof nodeInfo == "string"){
18640             return document.getElementById(nodeInfo);
18641         }else if(typeof nodeInfo == "number"){
18642             return this.nodes[nodeInfo];
18643         }
18644         return nodeInfo;
18645     },
18646
18647     /**
18648      * Gets a range template nodes.
18649      * @param {Number} startIndex
18650      * @param {Number} endIndex
18651      * @return {Array} An array of nodes
18652      */
18653     getNodes : function(start, end){
18654         var ns = this.nodes;
18655         start = start || 0;
18656         end = typeof end == "undefined" ? ns.length - 1 : end;
18657         var nodes = [];
18658         if(start <= end){
18659             for(var i = start; i <= end; i++){
18660                 nodes.push(ns[i]);
18661             }
18662         } else{
18663             for(var i = start; i >= end; i--){
18664                 nodes.push(ns[i]);
18665             }
18666         }
18667         return nodes;
18668     },
18669
18670     /**
18671      * Finds the index of the passed node
18672      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18673      * @return {Number} The index of the node or -1
18674      */
18675     indexOf : function(node){
18676         node = this.getNode(node);
18677         if(typeof node.nodeIndex == "number"){
18678             return node.nodeIndex;
18679         }
18680         var ns = this.nodes;
18681         for(var i = 0, len = ns.length; i < len; i++){
18682             if(ns[i] == node){
18683                 return i;
18684             }
18685         }
18686         return -1;
18687     }
18688 });
18689 /*
18690  * - LGPL
18691  *
18692  * based on jquery fullcalendar
18693  * 
18694  */
18695
18696 Roo.bootstrap = Roo.bootstrap || {};
18697 /**
18698  * @class Roo.bootstrap.Calendar
18699  * @extends Roo.bootstrap.Component
18700  * Bootstrap Calendar class
18701  * @cfg {Boolean} loadMask (true|false) default false
18702  * @cfg {Object} header generate the user specific header of the calendar, default false
18703
18704  * @constructor
18705  * Create a new Container
18706  * @param {Object} config The config object
18707  */
18708
18709
18710
18711 Roo.bootstrap.Calendar = function(config){
18712     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18713      this.addEvents({
18714         /**
18715              * @event select
18716              * Fires when a date is selected
18717              * @param {DatePicker} this
18718              * @param {Date} date The selected date
18719              */
18720         'select': true,
18721         /**
18722              * @event monthchange
18723              * Fires when the displayed month changes 
18724              * @param {DatePicker} this
18725              * @param {Date} date The selected month
18726              */
18727         'monthchange': true,
18728         /**
18729              * @event evententer
18730              * Fires when mouse over an event
18731              * @param {Calendar} this
18732              * @param {event} Event
18733              */
18734         'evententer': true,
18735         /**
18736              * @event eventleave
18737              * Fires when the mouse leaves an
18738              * @param {Calendar} this
18739              * @param {event}
18740              */
18741         'eventleave': true,
18742         /**
18743              * @event eventclick
18744              * Fires when the mouse click an
18745              * @param {Calendar} this
18746              * @param {event}
18747              */
18748         'eventclick': true
18749         
18750     });
18751
18752 };
18753
18754 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18755     
18756      /**
18757      * @cfg {Number} startDay
18758      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18759      */
18760     startDay : 0,
18761     
18762     loadMask : false,
18763     
18764     header : false,
18765       
18766     getAutoCreate : function(){
18767         
18768         
18769         var fc_button = function(name, corner, style, content ) {
18770             return Roo.apply({},{
18771                 tag : 'span',
18772                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18773                          (corner.length ?
18774                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18775                             ''
18776                         ),
18777                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18778                 unselectable: 'on'
18779             });
18780         };
18781         
18782         var header = {};
18783         
18784         if(!this.header){
18785             header = {
18786                 tag : 'table',
18787                 cls : 'fc-header',
18788                 style : 'width:100%',
18789                 cn : [
18790                     {
18791                         tag: 'tr',
18792                         cn : [
18793                             {
18794                                 tag : 'td',
18795                                 cls : 'fc-header-left',
18796                                 cn : [
18797                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18798                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18799                                     { tag: 'span', cls: 'fc-header-space' },
18800                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18801
18802
18803                                 ]
18804                             },
18805
18806                             {
18807                                 tag : 'td',
18808                                 cls : 'fc-header-center',
18809                                 cn : [
18810                                     {
18811                                         tag: 'span',
18812                                         cls: 'fc-header-title',
18813                                         cn : {
18814                                             tag: 'H2',
18815                                             html : 'month / year'
18816                                         }
18817                                     }
18818
18819                                 ]
18820                             },
18821                             {
18822                                 tag : 'td',
18823                                 cls : 'fc-header-right',
18824                                 cn : [
18825                               /*      fc_button('month', 'left', '', 'month' ),
18826                                     fc_button('week', '', '', 'week' ),
18827                                     fc_button('day', 'right', '', 'day' )
18828                                 */    
18829
18830                                 ]
18831                             }
18832
18833                         ]
18834                     }
18835                 ]
18836             };
18837         }
18838         
18839         header = this.header;
18840         
18841        
18842         var cal_heads = function() {
18843             var ret = [];
18844             // fixme - handle this.
18845             
18846             for (var i =0; i < Date.dayNames.length; i++) {
18847                 var d = Date.dayNames[i];
18848                 ret.push({
18849                     tag: 'th',
18850                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18851                     html : d.substring(0,3)
18852                 });
18853                 
18854             }
18855             ret[0].cls += ' fc-first';
18856             ret[6].cls += ' fc-last';
18857             return ret;
18858         };
18859         var cal_cell = function(n) {
18860             return  {
18861                 tag: 'td',
18862                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18863                 cn : [
18864                     {
18865                         cn : [
18866                             {
18867                                 cls: 'fc-day-number',
18868                                 html: 'D'
18869                             },
18870                             {
18871                                 cls: 'fc-day-content',
18872                              
18873                                 cn : [
18874                                      {
18875                                         style: 'position: relative;' // height: 17px;
18876                                     }
18877                                 ]
18878                             }
18879                             
18880                             
18881                         ]
18882                     }
18883                 ]
18884                 
18885             }
18886         };
18887         var cal_rows = function() {
18888             
18889             var ret = [];
18890             for (var r = 0; r < 6; r++) {
18891                 var row= {
18892                     tag : 'tr',
18893                     cls : 'fc-week',
18894                     cn : []
18895                 };
18896                 
18897                 for (var i =0; i < Date.dayNames.length; i++) {
18898                     var d = Date.dayNames[i];
18899                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18900
18901                 }
18902                 row.cn[0].cls+=' fc-first';
18903                 row.cn[0].cn[0].style = 'min-height:90px';
18904                 row.cn[6].cls+=' fc-last';
18905                 ret.push(row);
18906                 
18907             }
18908             ret[0].cls += ' fc-first';
18909             ret[4].cls += ' fc-prev-last';
18910             ret[5].cls += ' fc-last';
18911             return ret;
18912             
18913         };
18914         
18915         var cal_table = {
18916             tag: 'table',
18917             cls: 'fc-border-separate',
18918             style : 'width:100%',
18919             cellspacing  : 0,
18920             cn : [
18921                 { 
18922                     tag: 'thead',
18923                     cn : [
18924                         { 
18925                             tag: 'tr',
18926                             cls : 'fc-first fc-last',
18927                             cn : cal_heads()
18928                         }
18929                     ]
18930                 },
18931                 { 
18932                     tag: 'tbody',
18933                     cn : cal_rows()
18934                 }
18935                   
18936             ]
18937         };
18938          
18939          var cfg = {
18940             cls : 'fc fc-ltr',
18941             cn : [
18942                 header,
18943                 {
18944                     cls : 'fc-content',
18945                     style : "position: relative;",
18946                     cn : [
18947                         {
18948                             cls : 'fc-view fc-view-month fc-grid',
18949                             style : 'position: relative',
18950                             unselectable : 'on',
18951                             cn : [
18952                                 {
18953                                     cls : 'fc-event-container',
18954                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18955                                 },
18956                                 cal_table
18957                             ]
18958                         }
18959                     ]
18960     
18961                 }
18962            ] 
18963             
18964         };
18965         
18966          
18967         
18968         return cfg;
18969     },
18970     
18971     
18972     initEvents : function()
18973     {
18974         if(!this.store){
18975             throw "can not find store for calendar";
18976         }
18977         
18978         var mark = {
18979             tag: "div",
18980             cls:"x-dlg-mask",
18981             style: "text-align:center",
18982             cn: [
18983                 {
18984                     tag: "div",
18985                     style: "background-color:white;width:50%;margin:250 auto",
18986                     cn: [
18987                         {
18988                             tag: "img",
18989                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18990                         },
18991                         {
18992                             tag: "span",
18993                             html: "Loading"
18994                         }
18995                         
18996                     ]
18997                 }
18998             ]
18999         };
19000         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19001         
19002         var size = this.el.select('.fc-content', true).first().getSize();
19003         this.maskEl.setSize(size.width, size.height);
19004         this.maskEl.enableDisplayMode("block");
19005         if(!this.loadMask){
19006             this.maskEl.hide();
19007         }
19008         
19009         this.store = Roo.factory(this.store, Roo.data);
19010         this.store.on('load', this.onLoad, this);
19011         this.store.on('beforeload', this.onBeforeLoad, this);
19012         
19013         this.resize();
19014         
19015         this.cells = this.el.select('.fc-day',true);
19016         //Roo.log(this.cells);
19017         this.textNodes = this.el.query('.fc-day-number');
19018         this.cells.addClassOnOver('fc-state-hover');
19019         
19020         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19021         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19022         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19023         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19024         
19025         this.on('monthchange', this.onMonthChange, this);
19026         
19027         this.update(new Date().clearTime());
19028     },
19029     
19030     resize : function() {
19031         var sz  = this.el.getSize();
19032         
19033         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19034         this.el.select('.fc-day-content div',true).setHeight(34);
19035     },
19036     
19037     
19038     // private
19039     showPrevMonth : function(e){
19040         this.update(this.activeDate.add("mo", -1));
19041     },
19042     showToday : function(e){
19043         this.update(new Date().clearTime());
19044     },
19045     // private
19046     showNextMonth : function(e){
19047         this.update(this.activeDate.add("mo", 1));
19048     },
19049
19050     // private
19051     showPrevYear : function(){
19052         this.update(this.activeDate.add("y", -1));
19053     },
19054
19055     // private
19056     showNextYear : function(){
19057         this.update(this.activeDate.add("y", 1));
19058     },
19059
19060     
19061    // private
19062     update : function(date)
19063     {
19064         var vd = this.activeDate;
19065         this.activeDate = date;
19066 //        if(vd && this.el){
19067 //            var t = date.getTime();
19068 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19069 //                Roo.log('using add remove');
19070 //                
19071 //                this.fireEvent('monthchange', this, date);
19072 //                
19073 //                this.cells.removeClass("fc-state-highlight");
19074 //                this.cells.each(function(c){
19075 //                   if(c.dateValue == t){
19076 //                       c.addClass("fc-state-highlight");
19077 //                       setTimeout(function(){
19078 //                            try{c.dom.firstChild.focus();}catch(e){}
19079 //                       }, 50);
19080 //                       return false;
19081 //                   }
19082 //                   return true;
19083 //                });
19084 //                return;
19085 //            }
19086 //        }
19087         
19088         var days = date.getDaysInMonth();
19089         
19090         var firstOfMonth = date.getFirstDateOfMonth();
19091         var startingPos = firstOfMonth.getDay()-this.startDay;
19092         
19093         if(startingPos < this.startDay){
19094             startingPos += 7;
19095         }
19096         
19097         var pm = date.add(Date.MONTH, -1);
19098         var prevStart = pm.getDaysInMonth()-startingPos;
19099 //        
19100         this.cells = this.el.select('.fc-day',true);
19101         this.textNodes = this.el.query('.fc-day-number');
19102         this.cells.addClassOnOver('fc-state-hover');
19103         
19104         var cells = this.cells.elements;
19105         var textEls = this.textNodes;
19106         
19107         Roo.each(cells, function(cell){
19108             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19109         });
19110         
19111         days += startingPos;
19112
19113         // convert everything to numbers so it's fast
19114         var day = 86400000;
19115         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19116         //Roo.log(d);
19117         //Roo.log(pm);
19118         //Roo.log(prevStart);
19119         
19120         var today = new Date().clearTime().getTime();
19121         var sel = date.clearTime().getTime();
19122         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19123         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19124         var ddMatch = this.disabledDatesRE;
19125         var ddText = this.disabledDatesText;
19126         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19127         var ddaysText = this.disabledDaysText;
19128         var format = this.format;
19129         
19130         var setCellClass = function(cal, cell){
19131             cell.row = 0;
19132             cell.events = [];
19133             cell.more = [];
19134             //Roo.log('set Cell Class');
19135             cell.title = "";
19136             var t = d.getTime();
19137             
19138             //Roo.log(d);
19139             
19140             cell.dateValue = t;
19141             if(t == today){
19142                 cell.className += " fc-today";
19143                 cell.className += " fc-state-highlight";
19144                 cell.title = cal.todayText;
19145             }
19146             if(t == sel){
19147                 // disable highlight in other month..
19148                 //cell.className += " fc-state-highlight";
19149                 
19150             }
19151             // disabling
19152             if(t < min) {
19153                 cell.className = " fc-state-disabled";
19154                 cell.title = cal.minText;
19155                 return;
19156             }
19157             if(t > max) {
19158                 cell.className = " fc-state-disabled";
19159                 cell.title = cal.maxText;
19160                 return;
19161             }
19162             if(ddays){
19163                 if(ddays.indexOf(d.getDay()) != -1){
19164                     cell.title = ddaysText;
19165                     cell.className = " fc-state-disabled";
19166                 }
19167             }
19168             if(ddMatch && format){
19169                 var fvalue = d.dateFormat(format);
19170                 if(ddMatch.test(fvalue)){
19171                     cell.title = ddText.replace("%0", fvalue);
19172                     cell.className = " fc-state-disabled";
19173                 }
19174             }
19175             
19176             if (!cell.initialClassName) {
19177                 cell.initialClassName = cell.dom.className;
19178             }
19179             
19180             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19181         };
19182
19183         var i = 0;
19184         
19185         for(; i < startingPos; i++) {
19186             textEls[i].innerHTML = (++prevStart);
19187             d.setDate(d.getDate()+1);
19188             
19189             cells[i].className = "fc-past fc-other-month";
19190             setCellClass(this, cells[i]);
19191         }
19192         
19193         var intDay = 0;
19194         
19195         for(; i < days; i++){
19196             intDay = i - startingPos + 1;
19197             textEls[i].innerHTML = (intDay);
19198             d.setDate(d.getDate()+1);
19199             
19200             cells[i].className = ''; // "x-date-active";
19201             setCellClass(this, cells[i]);
19202         }
19203         var extraDays = 0;
19204         
19205         for(; i < 42; i++) {
19206             textEls[i].innerHTML = (++extraDays);
19207             d.setDate(d.getDate()+1);
19208             
19209             cells[i].className = "fc-future fc-other-month";
19210             setCellClass(this, cells[i]);
19211         }
19212         
19213         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19214         
19215         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19216         
19217         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19218         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19219         
19220         if(totalRows != 6){
19221             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19222             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19223         }
19224         
19225         this.fireEvent('monthchange', this, date);
19226         
19227         
19228         /*
19229         if(!this.internalRender){
19230             var main = this.el.dom.firstChild;
19231             var w = main.offsetWidth;
19232             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19233             Roo.fly(main).setWidth(w);
19234             this.internalRender = true;
19235             // opera does not respect the auto grow header center column
19236             // then, after it gets a width opera refuses to recalculate
19237             // without a second pass
19238             if(Roo.isOpera && !this.secondPass){
19239                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19240                 this.secondPass = true;
19241                 this.update.defer(10, this, [date]);
19242             }
19243         }
19244         */
19245         
19246     },
19247     
19248     findCell : function(dt) {
19249         dt = dt.clearTime().getTime();
19250         var ret = false;
19251         this.cells.each(function(c){
19252             //Roo.log("check " +c.dateValue + '?=' + dt);
19253             if(c.dateValue == dt){
19254                 ret = c;
19255                 return false;
19256             }
19257             return true;
19258         });
19259         
19260         return ret;
19261     },
19262     
19263     findCells : function(ev) {
19264         var s = ev.start.clone().clearTime().getTime();
19265        // Roo.log(s);
19266         var e= ev.end.clone().clearTime().getTime();
19267        // Roo.log(e);
19268         var ret = [];
19269         this.cells.each(function(c){
19270              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19271             
19272             if(c.dateValue > e){
19273                 return ;
19274             }
19275             if(c.dateValue < s){
19276                 return ;
19277             }
19278             ret.push(c);
19279         });
19280         
19281         return ret;    
19282     },
19283     
19284 //    findBestRow: function(cells)
19285 //    {
19286 //        var ret = 0;
19287 //        
19288 //        for (var i =0 ; i < cells.length;i++) {
19289 //            ret  = Math.max(cells[i].rows || 0,ret);
19290 //        }
19291 //        return ret;
19292 //        
19293 //    },
19294     
19295     
19296     addItem : function(ev)
19297     {
19298         // look for vertical location slot in
19299         var cells = this.findCells(ev);
19300         
19301 //        ev.row = this.findBestRow(cells);
19302         
19303         // work out the location.
19304         
19305         var crow = false;
19306         var rows = [];
19307         for(var i =0; i < cells.length; i++) {
19308             
19309             cells[i].row = cells[0].row;
19310             
19311             if(i == 0){
19312                 cells[i].row = cells[i].row + 1;
19313             }
19314             
19315             if (!crow) {
19316                 crow = {
19317                     start : cells[i],
19318                     end :  cells[i]
19319                 };
19320                 continue;
19321             }
19322             if (crow.start.getY() == cells[i].getY()) {
19323                 // on same row.
19324                 crow.end = cells[i];
19325                 continue;
19326             }
19327             // different row.
19328             rows.push(crow);
19329             crow = {
19330                 start: cells[i],
19331                 end : cells[i]
19332             };
19333             
19334         }
19335         
19336         rows.push(crow);
19337         ev.els = [];
19338         ev.rows = rows;
19339         ev.cells = cells;
19340         
19341         cells[0].events.push(ev);
19342         
19343         this.calevents.push(ev);
19344     },
19345     
19346     clearEvents: function() {
19347         
19348         if(!this.calevents){
19349             return;
19350         }
19351         
19352         Roo.each(this.cells.elements, function(c){
19353             c.row = 0;
19354             c.events = [];
19355             c.more = [];
19356         });
19357         
19358         Roo.each(this.calevents, function(e) {
19359             Roo.each(e.els, function(el) {
19360                 el.un('mouseenter' ,this.onEventEnter, this);
19361                 el.un('mouseleave' ,this.onEventLeave, this);
19362                 el.remove();
19363             },this);
19364         },this);
19365         
19366         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19367             e.remove();
19368         });
19369         
19370     },
19371     
19372     renderEvents: function()
19373     {   
19374         var _this = this;
19375         
19376         this.cells.each(function(c) {
19377             
19378             if(c.row < 5){
19379                 return;
19380             }
19381             
19382             var ev = c.events;
19383             
19384             var r = 4;
19385             if(c.row != c.events.length){
19386                 r = 4 - (4 - (c.row - c.events.length));
19387             }
19388             
19389             c.events = ev.slice(0, r);
19390             c.more = ev.slice(r);
19391             
19392             if(c.more.length && c.more.length == 1){
19393                 c.events.push(c.more.pop());
19394             }
19395             
19396             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19397             
19398         });
19399             
19400         this.cells.each(function(c) {
19401             
19402             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19403             
19404             
19405             for (var e = 0; e < c.events.length; e++){
19406                 var ev = c.events[e];
19407                 var rows = ev.rows;
19408                 
19409                 for(var i = 0; i < rows.length; i++) {
19410                 
19411                     // how many rows should it span..
19412
19413                     var  cfg = {
19414                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19415                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19416
19417                         unselectable : "on",
19418                         cn : [
19419                             {
19420                                 cls: 'fc-event-inner',
19421                                 cn : [
19422     //                                {
19423     //                                  tag:'span',
19424     //                                  cls: 'fc-event-time',
19425     //                                  html : cells.length > 1 ? '' : ev.time
19426     //                                },
19427                                     {
19428                                       tag:'span',
19429                                       cls: 'fc-event-title',
19430                                       html : String.format('{0}', ev.title)
19431                                     }
19432
19433
19434                                 ]
19435                             },
19436                             {
19437                                 cls: 'ui-resizable-handle ui-resizable-e',
19438                                 html : '&nbsp;&nbsp;&nbsp'
19439                             }
19440
19441                         ]
19442                     };
19443
19444                     if (i == 0) {
19445                         cfg.cls += ' fc-event-start';
19446                     }
19447                     if ((i+1) == rows.length) {
19448                         cfg.cls += ' fc-event-end';
19449                     }
19450
19451                     var ctr = _this.el.select('.fc-event-container',true).first();
19452                     var cg = ctr.createChild(cfg);
19453
19454                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19455                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19456
19457                     var r = (c.more.length) ? 1 : 0;
19458                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19459                     cg.setWidth(ebox.right - sbox.x -2);
19460
19461                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19462                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19463                     cg.on('click', _this.onEventClick, _this, ev);
19464
19465                     ev.els.push(cg);
19466                     
19467                 }
19468                 
19469             }
19470             
19471             
19472             if(c.more.length){
19473                 var  cfg = {
19474                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19475                     style : 'position: absolute',
19476                     unselectable : "on",
19477                     cn : [
19478                         {
19479                             cls: 'fc-event-inner',
19480                             cn : [
19481                                 {
19482                                   tag:'span',
19483                                   cls: 'fc-event-title',
19484                                   html : 'More'
19485                                 }
19486
19487
19488                             ]
19489                         },
19490                         {
19491                             cls: 'ui-resizable-handle ui-resizable-e',
19492                             html : '&nbsp;&nbsp;&nbsp'
19493                         }
19494
19495                     ]
19496                 };
19497
19498                 var ctr = _this.el.select('.fc-event-container',true).first();
19499                 var cg = ctr.createChild(cfg);
19500
19501                 var sbox = c.select('.fc-day-content',true).first().getBox();
19502                 var ebox = c.select('.fc-day-content',true).first().getBox();
19503                 //Roo.log(cg);
19504                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19505                 cg.setWidth(ebox.right - sbox.x -2);
19506
19507                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19508                 
19509             }
19510             
19511         });
19512         
19513         
19514         
19515     },
19516     
19517     onEventEnter: function (e, el,event,d) {
19518         this.fireEvent('evententer', this, el, event);
19519     },
19520     
19521     onEventLeave: function (e, el,event,d) {
19522         this.fireEvent('eventleave', this, el, event);
19523     },
19524     
19525     onEventClick: function (e, el,event,d) {
19526         this.fireEvent('eventclick', this, el, event);
19527     },
19528     
19529     onMonthChange: function () {
19530         this.store.load();
19531     },
19532     
19533     onMoreEventClick: function(e, el, more)
19534     {
19535         var _this = this;
19536         
19537         this.calpopover.placement = 'right';
19538         this.calpopover.setTitle('More');
19539         
19540         this.calpopover.setContent('');
19541         
19542         var ctr = this.calpopover.el.select('.popover-content', true).first();
19543         
19544         Roo.each(more, function(m){
19545             var cfg = {
19546                 cls : 'fc-event-hori fc-event-draggable',
19547                 html : m.title
19548             };
19549             var cg = ctr.createChild(cfg);
19550             
19551             cg.on('click', _this.onEventClick, _this, m);
19552         });
19553         
19554         this.calpopover.show(el);
19555         
19556         
19557     },
19558     
19559     onLoad: function () 
19560     {   
19561         this.calevents = [];
19562         var cal = this;
19563         
19564         if(this.store.getCount() > 0){
19565             this.store.data.each(function(d){
19566                cal.addItem({
19567                     id : d.data.id,
19568                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19569                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19570                     time : d.data.start_time,
19571                     title : d.data.title,
19572                     description : d.data.description,
19573                     venue : d.data.venue
19574                 });
19575             });
19576         }
19577         
19578         this.renderEvents();
19579         
19580         if(this.calevents.length && this.loadMask){
19581             this.maskEl.hide();
19582         }
19583     },
19584     
19585     onBeforeLoad: function()
19586     {
19587         this.clearEvents();
19588         if(this.loadMask){
19589             this.maskEl.show();
19590         }
19591     }
19592 });
19593
19594  
19595  /*
19596  * - LGPL
19597  *
19598  * element
19599  * 
19600  */
19601
19602 /**
19603  * @class Roo.bootstrap.Popover
19604  * @extends Roo.bootstrap.Component
19605  * Bootstrap Popover class
19606  * @cfg {String} html contents of the popover   (or false to use children..)
19607  * @cfg {String} title of popover (or false to hide)
19608  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19609  * @cfg {String} trigger click || hover (or false to trigger manually)
19610  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19611  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19612  *      - if false and it has a 'parent' then it will be automatically added to that element
19613  *      - if string - Roo.get  will be called 
19614  * @cfg {Number} delay - delay before showing
19615  
19616  * @constructor
19617  * Create a new Popover
19618  * @param {Object} config The config object
19619  */
19620
19621 Roo.bootstrap.Popover = function(config){
19622     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19623     
19624     this.addEvents({
19625         // raw events
19626          /**
19627          * @event show
19628          * After the popover show
19629          * 
19630          * @param {Roo.bootstrap.Popover} this
19631          */
19632         "show" : true,
19633         /**
19634          * @event hide
19635          * After the popover hide
19636          * 
19637          * @param {Roo.bootstrap.Popover} this
19638          */
19639         "hide" : true
19640     });
19641 };
19642
19643 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19644     
19645     title: false,
19646     html: false,
19647     
19648     placement : 'right',
19649     trigger : 'hover', // hover
19650     modal : false,
19651     delay : 0,
19652     
19653     over: false,
19654     
19655     can_build_overlaid : false,
19656     
19657     maskEl : false, // the mask element
19658     headerEl : false,
19659     contentEl : false,
19660     
19661     
19662     getChildContainer : function()
19663     {
19664         return this.contentEl;
19665         
19666     },
19667     getPopoverHeader : function()
19668     {
19669         this.title = true; // flag not to hide it..
19670         return this.headerEl
19671     },
19672     
19673     
19674     getAutoCreate : function(){
19675          
19676         var cfg = {
19677            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19678            style: 'display:block',
19679            cn : [
19680                 {
19681                     cls : 'arrow'
19682                 },
19683                 {
19684                     cls : 'popover-inner ',
19685                     cn : [
19686                         {
19687                             tag: 'h3',
19688                             cls: 'popover-title popover-header',
19689                             html : this.title === false ? '' : this.title
19690                         },
19691                         {
19692                             cls : 'popover-content popover-body '  + (this.cls || ''),
19693                             html : this.html || ''
19694                         }
19695                     ]
19696                     
19697                 }
19698            ]
19699         };
19700         
19701         return cfg;
19702     },
19703     /**
19704      * @param {string} the title
19705      */
19706     setTitle: function(str)
19707     {
19708         this.title = str;
19709         if (this.el) {
19710             this.headerEl.dom.innerHTML = str;
19711         }
19712         
19713     },
19714     /**
19715      * @param {string} the body content
19716      */
19717     setContent: function(str)
19718     {
19719         this.html = str;
19720         if (this.contentEl) {
19721             this.contentEl.dom.innerHTML = str;
19722         }
19723         
19724     },
19725     // as it get's added to the bottom of the page.
19726     onRender : function(ct, position)
19727     {
19728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19729         
19730         
19731         
19732         if(!this.el){
19733             var cfg = Roo.apply({},  this.getAutoCreate());
19734             cfg.id = Roo.id();
19735             
19736             if (this.cls) {
19737                 cfg.cls += ' ' + this.cls;
19738             }
19739             if (this.style) {
19740                 cfg.style = this.style;
19741             }
19742             //Roo.log("adding to ");
19743             this.el = Roo.get(document.body).createChild(cfg, position);
19744 //            Roo.log(this.el);
19745         }
19746         
19747         this.contentEl = this.el.select('.popover-content',true).first();
19748         this.headerEl =  this.el.select('.popover-title',true).first();
19749         
19750         var nitems = [];
19751         if(typeof(this.items) != 'undefined'){
19752             var items = this.items;
19753             delete this.items;
19754
19755             for(var i =0;i < items.length;i++) {
19756                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19757             }
19758         }
19759
19760         this.items = nitems;
19761         
19762         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19763         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19764         
19765         
19766         
19767         this.initEvents();
19768     },
19769     
19770     resizeMask : function()
19771     {
19772         this.maskEl.setSize(
19773             Roo.lib.Dom.getViewWidth(true),
19774             Roo.lib.Dom.getViewHeight(true)
19775         );
19776     },
19777     
19778     initEvents : function()
19779     {
19780         
19781         if (!this.modal) { 
19782             Roo.bootstrap.Popover.register(this);
19783         }
19784          
19785         
19786         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19787         this.el.enableDisplayMode('block');
19788         this.el.hide();
19789         if (this.over === false && !this.parent()) {
19790             return; 
19791         }
19792         if (this.triggers === false) {
19793             return;
19794         }
19795          
19796         // support parent
19797         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19798         var triggers = this.trigger ? this.trigger.split(' ') : [];
19799         Roo.each(triggers, function(trigger) {
19800         
19801             if (trigger == 'click') {
19802                 on_el.on('click', this.toggle, this);
19803             } else if (trigger != 'manual') {
19804                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19805                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19806       
19807                 on_el.on(eventIn  ,this.enter, this);
19808                 on_el.on(eventOut, this.leave, this);
19809             }
19810         }, this);
19811         
19812     },
19813     
19814     
19815     // private
19816     timeout : null,
19817     hoverState : null,
19818     
19819     toggle : function () {
19820         this.hoverState == 'in' ? this.leave() : this.enter();
19821     },
19822     
19823     enter : function () {
19824         
19825         clearTimeout(this.timeout);
19826     
19827         this.hoverState = 'in';
19828     
19829         if (!this.delay || !this.delay.show) {
19830             this.show();
19831             return;
19832         }
19833         var _t = this;
19834         this.timeout = setTimeout(function () {
19835             if (_t.hoverState == 'in') {
19836                 _t.show();
19837             }
19838         }, this.delay.show)
19839     },
19840     
19841     leave : function() {
19842         clearTimeout(this.timeout);
19843     
19844         this.hoverState = 'out';
19845     
19846         if (!this.delay || !this.delay.hide) {
19847             this.hide();
19848             return;
19849         }
19850         var _t = this;
19851         this.timeout = setTimeout(function () {
19852             if (_t.hoverState == 'out') {
19853                 _t.hide();
19854             }
19855         }, this.delay.hide)
19856     },
19857     /**
19858      * Show the popover
19859      * @param {Roo.Element|string|false} - element to align and point to.
19860      */
19861     show : function (on_el)
19862     {
19863         
19864         on_el = on_el || false; // default to false
19865         if (!on_el) {
19866             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19867                 on_el = this.parent().el;
19868             } else if (this.over) {
19869                 Roo.get(this.over);
19870             }
19871             
19872         }
19873         
19874         if (!this.el) {
19875             this.render(document.body);
19876         }
19877         
19878         
19879         this.el.removeClass([
19880             'fade','top','bottom', 'left', 'right','in',
19881             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19882         ]);
19883         
19884         if (this.title === false) {
19885             this.headerEl.hide();
19886         }
19887         
19888         
19889         var placement = typeof this.placement == 'function' ?
19890             this.placement.call(this, this.el, on_el) :
19891             this.placement;
19892             
19893         /*
19894         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19895         
19896         // I think  'auto right' - but 
19897         
19898         var autoPlace = autoToken.test(placement);
19899         if (autoPlace) {
19900             placement = placement.replace(autoToken, '') || 'top';
19901         }
19902         */
19903         
19904         
19905         this.el.show();
19906         this.el.dom.style.display='block';
19907         
19908         //this.el.appendTo(on_el);
19909         
19910         var p = this.getPosition();
19911         var box = this.el.getBox();
19912         
19913         
19914         var align = Roo.bootstrap.Popover.alignment[placement];
19915         this.el.addClass(align[2]);
19916
19917 //        Roo.log(align);
19918
19919         if (on_el) {
19920             this.el.alignTo(on_el, align[0],align[1]);
19921         } else {
19922             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19923             var es = this.el.getSize();
19924             var x = Roo.lib.Dom.getViewWidth()/2;
19925             var y = Roo.lib.Dom.getViewHeight()/2;
19926             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19927             
19928         }
19929
19930         
19931         //var arrow = this.el.select('.arrow',true).first();
19932         //arrow.set(align[2], 
19933         
19934         this.el.addClass('in');
19935         
19936         
19937         if (this.el.hasClass('fade')) {
19938             // fade it?
19939         }
19940         
19941         this.hoverState = 'in';
19942         
19943         if (this.modal) {
19944             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19945             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19946             this.maskEl.dom.style.display = 'block';
19947             this.maskEl.addClass('show');
19948         }
19949         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19950
19951         
19952         
19953         this.fireEvent('show', this);
19954         
19955     },
19956     hide : function()
19957     {
19958         this.el.setXY([0,0]);
19959         this.el.removeClass('in');
19960         this.el.hide();
19961         this.hoverState = null;
19962         this.maskEl.hide(); // always..
19963         this.fireEvent('hide', this);
19964     }
19965     
19966 });
19967
19968
19969 Roo.apply(Roo.bootstrap.Popover, {
19970
19971     alignment : {
19972         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19973         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19974         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19975         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19976     },
19977     
19978     zIndex : 20001,
19979
19980     clickHander : false,
19981     
19982
19983     onMouseDown : function(e)
19984     {
19985         if (!e.getTarget(".roo-popover")) {
19986             this.hideAll();
19987         }
19988          
19989     },
19990     
19991     popups : [],
19992     
19993     register : function(popup)
19994     {
19995         if (!Roo.bootstrap.Popover.clickHandler) {
19996             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19997         }
19998         // hide other popups.
19999         this.hideAll();
20000         this.popups.push(popup);
20001     },
20002     hideAll : function()
20003     {
20004         this.popups.forEach(function(p) {
20005             p.hide();
20006         });
20007     }
20008
20009 });/*
20010  * - LGPL
20011  *
20012  * Card header - holder for the card header elements.
20013  * 
20014  */
20015
20016 /**
20017  * @class Roo.bootstrap.PopoverNav
20018  * @extends Roo.bootstrap.NavGroup
20019  * Bootstrap Popover header navigation class
20020  * @constructor
20021  * Create a new Popover Header Navigation 
20022  * @param {Object} config The config object
20023  */
20024
20025 Roo.bootstrap.PopoverNav = function(config){
20026     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20027 };
20028
20029 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20030     
20031     
20032     container_method : 'getPopoverHeader' 
20033     
20034      
20035     
20036     
20037    
20038 });
20039
20040  
20041
20042  /*
20043  * - LGPL
20044  *
20045  * Progress
20046  * 
20047  */
20048
20049 /**
20050  * @class Roo.bootstrap.Progress
20051  * @extends Roo.bootstrap.Component
20052  * Bootstrap Progress class
20053  * @cfg {Boolean} striped striped of the progress bar
20054  * @cfg {Boolean} active animated of the progress bar
20055  * 
20056  * 
20057  * @constructor
20058  * Create a new Progress
20059  * @param {Object} config The config object
20060  */
20061
20062 Roo.bootstrap.Progress = function(config){
20063     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20064 };
20065
20066 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20067     
20068     striped : false,
20069     active: false,
20070     
20071     getAutoCreate : function(){
20072         var cfg = {
20073             tag: 'div',
20074             cls: 'progress'
20075         };
20076         
20077         
20078         if(this.striped){
20079             cfg.cls += ' progress-striped';
20080         }
20081       
20082         if(this.active){
20083             cfg.cls += ' active';
20084         }
20085         
20086         
20087         return cfg;
20088     }
20089    
20090 });
20091
20092  
20093
20094  /*
20095  * - LGPL
20096  *
20097  * ProgressBar
20098  * 
20099  */
20100
20101 /**
20102  * @class Roo.bootstrap.ProgressBar
20103  * @extends Roo.bootstrap.Component
20104  * Bootstrap ProgressBar class
20105  * @cfg {Number} aria_valuenow aria-value now
20106  * @cfg {Number} aria_valuemin aria-value min
20107  * @cfg {Number} aria_valuemax aria-value max
20108  * @cfg {String} label label for the progress bar
20109  * @cfg {String} panel (success | info | warning | danger )
20110  * @cfg {String} role role of the progress bar
20111  * @cfg {String} sr_only text
20112  * 
20113  * 
20114  * @constructor
20115  * Create a new ProgressBar
20116  * @param {Object} config The config object
20117  */
20118
20119 Roo.bootstrap.ProgressBar = function(config){
20120     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20121 };
20122
20123 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20124     
20125     aria_valuenow : 0,
20126     aria_valuemin : 0,
20127     aria_valuemax : 100,
20128     label : false,
20129     panel : false,
20130     role : false,
20131     sr_only: false,
20132     
20133     getAutoCreate : function()
20134     {
20135         
20136         var cfg = {
20137             tag: 'div',
20138             cls: 'progress-bar',
20139             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20140         };
20141         
20142         if(this.sr_only){
20143             cfg.cn = {
20144                 tag: 'span',
20145                 cls: 'sr-only',
20146                 html: this.sr_only
20147             }
20148         }
20149         
20150         if(this.role){
20151             cfg.role = this.role;
20152         }
20153         
20154         if(this.aria_valuenow){
20155             cfg['aria-valuenow'] = this.aria_valuenow;
20156         }
20157         
20158         if(this.aria_valuemin){
20159             cfg['aria-valuemin'] = this.aria_valuemin;
20160         }
20161         
20162         if(this.aria_valuemax){
20163             cfg['aria-valuemax'] = this.aria_valuemax;
20164         }
20165         
20166         if(this.label && !this.sr_only){
20167             cfg.html = this.label;
20168         }
20169         
20170         if(this.panel){
20171             cfg.cls += ' progress-bar-' + this.panel;
20172         }
20173         
20174         return cfg;
20175     },
20176     
20177     update : function(aria_valuenow)
20178     {
20179         this.aria_valuenow = aria_valuenow;
20180         
20181         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20182     }
20183    
20184 });
20185
20186  
20187
20188  /*
20189  * - LGPL
20190  *
20191  * column
20192  * 
20193  */
20194
20195 /**
20196  * @class Roo.bootstrap.TabGroup
20197  * @extends Roo.bootstrap.Column
20198  * Bootstrap Column class
20199  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20200  * @cfg {Boolean} carousel true to make the group behave like a carousel
20201  * @cfg {Boolean} bullets show bullets for the panels
20202  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20203  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20204  * @cfg {Boolean} showarrow (true|false) show arrow default true
20205  * 
20206  * @constructor
20207  * Create a new TabGroup
20208  * @param {Object} config The config object
20209  */
20210
20211 Roo.bootstrap.TabGroup = function(config){
20212     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20213     if (!this.navId) {
20214         this.navId = Roo.id();
20215     }
20216     this.tabs = [];
20217     Roo.bootstrap.TabGroup.register(this);
20218     
20219 };
20220
20221 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20222     
20223     carousel : false,
20224     transition : false,
20225     bullets : 0,
20226     timer : 0,
20227     autoslide : false,
20228     slideFn : false,
20229     slideOnTouch : false,
20230     showarrow : true,
20231     
20232     getAutoCreate : function()
20233     {
20234         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20235         
20236         cfg.cls += ' tab-content';
20237         
20238         if (this.carousel) {
20239             cfg.cls += ' carousel slide';
20240             
20241             cfg.cn = [{
20242                cls : 'carousel-inner',
20243                cn : []
20244             }];
20245         
20246             if(this.bullets  && !Roo.isTouch){
20247                 
20248                 var bullets = {
20249                     cls : 'carousel-bullets',
20250                     cn : []
20251                 };
20252                
20253                 if(this.bullets_cls){
20254                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20255                 }
20256                 
20257                 bullets.cn.push({
20258                     cls : 'clear'
20259                 });
20260                 
20261                 cfg.cn[0].cn.push(bullets);
20262             }
20263             
20264             if(this.showarrow){
20265                 cfg.cn[0].cn.push({
20266                     tag : 'div',
20267                     class : 'carousel-arrow',
20268                     cn : [
20269                         {
20270                             tag : 'div',
20271                             class : 'carousel-prev',
20272                             cn : [
20273                                 {
20274                                     tag : 'i',
20275                                     class : 'fa fa-chevron-left'
20276                                 }
20277                             ]
20278                         },
20279                         {
20280                             tag : 'div',
20281                             class : 'carousel-next',
20282                             cn : [
20283                                 {
20284                                     tag : 'i',
20285                                     class : 'fa fa-chevron-right'
20286                                 }
20287                             ]
20288                         }
20289                     ]
20290                 });
20291             }
20292             
20293         }
20294         
20295         return cfg;
20296     },
20297     
20298     initEvents:  function()
20299     {
20300 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20301 //            this.el.on("touchstart", this.onTouchStart, this);
20302 //        }
20303         
20304         if(this.autoslide){
20305             var _this = this;
20306             
20307             this.slideFn = window.setInterval(function() {
20308                 _this.showPanelNext();
20309             }, this.timer);
20310         }
20311         
20312         if(this.showarrow){
20313             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20314             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20315         }
20316         
20317         
20318     },
20319     
20320 //    onTouchStart : function(e, el, o)
20321 //    {
20322 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20323 //            return;
20324 //        }
20325 //        
20326 //        this.showPanelNext();
20327 //    },
20328     
20329     
20330     getChildContainer : function()
20331     {
20332         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20333     },
20334     
20335     /**
20336     * register a Navigation item
20337     * @param {Roo.bootstrap.NavItem} the navitem to add
20338     */
20339     register : function(item)
20340     {
20341         this.tabs.push( item);
20342         item.navId = this.navId; // not really needed..
20343         this.addBullet();
20344     
20345     },
20346     
20347     getActivePanel : function()
20348     {
20349         var r = false;
20350         Roo.each(this.tabs, function(t) {
20351             if (t.active) {
20352                 r = t;
20353                 return false;
20354             }
20355             return null;
20356         });
20357         return r;
20358         
20359     },
20360     getPanelByName : function(n)
20361     {
20362         var r = false;
20363         Roo.each(this.tabs, function(t) {
20364             if (t.tabId == n) {
20365                 r = t;
20366                 return false;
20367             }
20368             return null;
20369         });
20370         return r;
20371     },
20372     indexOfPanel : function(p)
20373     {
20374         var r = false;
20375         Roo.each(this.tabs, function(t,i) {
20376             if (t.tabId == p.tabId) {
20377                 r = i;
20378                 return false;
20379             }
20380             return null;
20381         });
20382         return r;
20383     },
20384     /**
20385      * show a specific panel
20386      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20387      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20388      */
20389     showPanel : function (pan)
20390     {
20391         if(this.transition || typeof(pan) == 'undefined'){
20392             Roo.log("waiting for the transitionend");
20393             return false;
20394         }
20395         
20396         if (typeof(pan) == 'number') {
20397             pan = this.tabs[pan];
20398         }
20399         
20400         if (typeof(pan) == 'string') {
20401             pan = this.getPanelByName(pan);
20402         }
20403         
20404         var cur = this.getActivePanel();
20405         
20406         if(!pan || !cur){
20407             Roo.log('pan or acitve pan is undefined');
20408             return false;
20409         }
20410         
20411         if (pan.tabId == this.getActivePanel().tabId) {
20412             return true;
20413         }
20414         
20415         if (false === cur.fireEvent('beforedeactivate')) {
20416             return false;
20417         }
20418         
20419         if(this.bullets > 0 && !Roo.isTouch){
20420             this.setActiveBullet(this.indexOfPanel(pan));
20421         }
20422         
20423         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20424             
20425             //class="carousel-item carousel-item-next carousel-item-left"
20426             
20427             this.transition = true;
20428             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20429             var lr = dir == 'next' ? 'left' : 'right';
20430             pan.el.addClass(dir); // or prev
20431             pan.el.addClass('carousel-item-' + dir); // or prev
20432             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20433             cur.el.addClass(lr); // or right
20434             pan.el.addClass(lr);
20435             cur.el.addClass('carousel-item-' +lr); // or right
20436             pan.el.addClass('carousel-item-' +lr);
20437             
20438             
20439             var _this = this;
20440             cur.el.on('transitionend', function() {
20441                 Roo.log("trans end?");
20442                 
20443                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20444                 pan.setActive(true);
20445                 
20446                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20447                 cur.setActive(false);
20448                 
20449                 _this.transition = false;
20450                 
20451             }, this, { single:  true } );
20452             
20453             return true;
20454         }
20455         
20456         cur.setActive(false);
20457         pan.setActive(true);
20458         
20459         return true;
20460         
20461     },
20462     showPanelNext : function()
20463     {
20464         var i = this.indexOfPanel(this.getActivePanel());
20465         
20466         if (i >= this.tabs.length - 1 && !this.autoslide) {
20467             return;
20468         }
20469         
20470         if (i >= this.tabs.length - 1 && this.autoslide) {
20471             i = -1;
20472         }
20473         
20474         this.showPanel(this.tabs[i+1]);
20475     },
20476     
20477     showPanelPrev : function()
20478     {
20479         var i = this.indexOfPanel(this.getActivePanel());
20480         
20481         if (i  < 1 && !this.autoslide) {
20482             return;
20483         }
20484         
20485         if (i < 1 && this.autoslide) {
20486             i = this.tabs.length;
20487         }
20488         
20489         this.showPanel(this.tabs[i-1]);
20490     },
20491     
20492     
20493     addBullet: function()
20494     {
20495         if(!this.bullets || Roo.isTouch){
20496             return;
20497         }
20498         var ctr = this.el.select('.carousel-bullets',true).first();
20499         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20500         var bullet = ctr.createChild({
20501             cls : 'bullet bullet-' + i
20502         },ctr.dom.lastChild);
20503         
20504         
20505         var _this = this;
20506         
20507         bullet.on('click', (function(e, el, o, ii, t){
20508
20509             e.preventDefault();
20510
20511             this.showPanel(ii);
20512
20513             if(this.autoslide && this.slideFn){
20514                 clearInterval(this.slideFn);
20515                 this.slideFn = window.setInterval(function() {
20516                     _this.showPanelNext();
20517                 }, this.timer);
20518             }
20519
20520         }).createDelegate(this, [i, bullet], true));
20521                 
20522         
20523     },
20524      
20525     setActiveBullet : function(i)
20526     {
20527         if(Roo.isTouch){
20528             return;
20529         }
20530         
20531         Roo.each(this.el.select('.bullet', true).elements, function(el){
20532             el.removeClass('selected');
20533         });
20534
20535         var bullet = this.el.select('.bullet-' + i, true).first();
20536         
20537         if(!bullet){
20538             return;
20539         }
20540         
20541         bullet.addClass('selected');
20542     }
20543     
20544     
20545   
20546 });
20547
20548  
20549
20550  
20551  
20552 Roo.apply(Roo.bootstrap.TabGroup, {
20553     
20554     groups: {},
20555      /**
20556     * register a Navigation Group
20557     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20558     */
20559     register : function(navgrp)
20560     {
20561         this.groups[navgrp.navId] = navgrp;
20562         
20563     },
20564     /**
20565     * fetch a Navigation Group based on the navigation ID
20566     * if one does not exist , it will get created.
20567     * @param {string} the navgroup to add
20568     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20569     */
20570     get: function(navId) {
20571         if (typeof(this.groups[navId]) == 'undefined') {
20572             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20573         }
20574         return this.groups[navId] ;
20575     }
20576     
20577     
20578     
20579 });
20580
20581  /*
20582  * - LGPL
20583  *
20584  * TabPanel
20585  * 
20586  */
20587
20588 /**
20589  * @class Roo.bootstrap.TabPanel
20590  * @extends Roo.bootstrap.Component
20591  * Bootstrap TabPanel class
20592  * @cfg {Boolean} active panel active
20593  * @cfg {String} html panel content
20594  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20595  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20596  * @cfg {String} href click to link..
20597  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20598  * 
20599  * 
20600  * @constructor
20601  * Create a new TabPanel
20602  * @param {Object} config The config object
20603  */
20604
20605 Roo.bootstrap.TabPanel = function(config){
20606     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20607     this.addEvents({
20608         /**
20609              * @event changed
20610              * Fires when the active status changes
20611              * @param {Roo.bootstrap.TabPanel} this
20612              * @param {Boolean} state the new state
20613             
20614          */
20615         'changed': true,
20616         /**
20617              * @event beforedeactivate
20618              * Fires before a tab is de-activated - can be used to do validation on a form.
20619              * @param {Roo.bootstrap.TabPanel} this
20620              * @return {Boolean} false if there is an error
20621             
20622          */
20623         'beforedeactivate': true
20624      });
20625     
20626     this.tabId = this.tabId || Roo.id();
20627   
20628 };
20629
20630 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20631     
20632     active: false,
20633     html: false,
20634     tabId: false,
20635     navId : false,
20636     href : '',
20637     touchSlide : false,
20638     getAutoCreate : function(){
20639         
20640         
20641         var cfg = {
20642             tag: 'div',
20643             // item is needed for carousel - not sure if it has any effect otherwise
20644             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20645             html: this.html || ''
20646         };
20647         
20648         if(this.active){
20649             cfg.cls += ' active';
20650         }
20651         
20652         if(this.tabId){
20653             cfg.tabId = this.tabId;
20654         }
20655         
20656         
20657         
20658         return cfg;
20659     },
20660     
20661     initEvents:  function()
20662     {
20663         var p = this.parent();
20664         
20665         this.navId = this.navId || p.navId;
20666         
20667         if (typeof(this.navId) != 'undefined') {
20668             // not really needed.. but just in case.. parent should be a NavGroup.
20669             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20670             
20671             tg.register(this);
20672             
20673             var i = tg.tabs.length - 1;
20674             
20675             if(this.active && tg.bullets > 0 && i < tg.bullets){
20676                 tg.setActiveBullet(i);
20677             }
20678         }
20679         
20680         this.el.on('click', this.onClick, this);
20681         
20682         if(Roo.isTouch && this.touchSlide){
20683             this.el.on("touchstart", this.onTouchStart, this);
20684             this.el.on("touchmove", this.onTouchMove, this);
20685             this.el.on("touchend", this.onTouchEnd, this);
20686         }
20687         
20688     },
20689     
20690     onRender : function(ct, position)
20691     {
20692         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20693     },
20694     
20695     setActive : function(state)
20696     {
20697         Roo.log("panel - set active " + this.tabId + "=" + state);
20698         
20699         this.active = state;
20700         if (!state) {
20701             this.el.removeClass('active');
20702             
20703         } else  if (!this.el.hasClass('active')) {
20704             this.el.addClass('active');
20705         }
20706         
20707         this.fireEvent('changed', this, state);
20708     },
20709     
20710     onClick : function(e)
20711     {
20712         e.preventDefault();
20713         
20714         if(!this.href.length){
20715             return;
20716         }
20717         
20718         window.location.href = this.href;
20719     },
20720     
20721     startX : 0,
20722     startY : 0,
20723     endX : 0,
20724     endY : 0,
20725     swiping : false,
20726     
20727     onTouchStart : function(e)
20728     {
20729         this.swiping = false;
20730         
20731         this.startX = e.browserEvent.touches[0].clientX;
20732         this.startY = e.browserEvent.touches[0].clientY;
20733     },
20734     
20735     onTouchMove : function(e)
20736     {
20737         this.swiping = true;
20738         
20739         this.endX = e.browserEvent.touches[0].clientX;
20740         this.endY = e.browserEvent.touches[0].clientY;
20741     },
20742     
20743     onTouchEnd : function(e)
20744     {
20745         if(!this.swiping){
20746             this.onClick(e);
20747             return;
20748         }
20749         
20750         var tabGroup = this.parent();
20751         
20752         if(this.endX > this.startX){ // swiping right
20753             tabGroup.showPanelPrev();
20754             return;
20755         }
20756         
20757         if(this.startX > this.endX){ // swiping left
20758             tabGroup.showPanelNext();
20759             return;
20760         }
20761     }
20762     
20763     
20764 });
20765  
20766
20767  
20768
20769  /*
20770  * - LGPL
20771  *
20772  * DateField
20773  * 
20774  */
20775
20776 /**
20777  * @class Roo.bootstrap.DateField
20778  * @extends Roo.bootstrap.Input
20779  * Bootstrap DateField class
20780  * @cfg {Number} weekStart default 0
20781  * @cfg {String} viewMode default empty, (months|years)
20782  * @cfg {String} minViewMode default empty, (months|years)
20783  * @cfg {Number} startDate default -Infinity
20784  * @cfg {Number} endDate default Infinity
20785  * @cfg {Boolean} todayHighlight default false
20786  * @cfg {Boolean} todayBtn default false
20787  * @cfg {Boolean} calendarWeeks default false
20788  * @cfg {Object} daysOfWeekDisabled default empty
20789  * @cfg {Boolean} singleMode default false (true | false)
20790  * 
20791  * @cfg {Boolean} keyboardNavigation default true
20792  * @cfg {String} language default en
20793  * 
20794  * @constructor
20795  * Create a new DateField
20796  * @param {Object} config The config object
20797  */
20798
20799 Roo.bootstrap.DateField = function(config){
20800     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20801      this.addEvents({
20802             /**
20803              * @event show
20804              * Fires when this field show.
20805              * @param {Roo.bootstrap.DateField} this
20806              * @param {Mixed} date The date value
20807              */
20808             show : true,
20809             /**
20810              * @event show
20811              * Fires when this field hide.
20812              * @param {Roo.bootstrap.DateField} this
20813              * @param {Mixed} date The date value
20814              */
20815             hide : true,
20816             /**
20817              * @event select
20818              * Fires when select a date.
20819              * @param {Roo.bootstrap.DateField} this
20820              * @param {Mixed} date The date value
20821              */
20822             select : true,
20823             /**
20824              * @event beforeselect
20825              * Fires when before select a date.
20826              * @param {Roo.bootstrap.DateField} this
20827              * @param {Mixed} date The date value
20828              */
20829             beforeselect : true
20830         });
20831 };
20832
20833 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20834     
20835     /**
20836      * @cfg {String} format
20837      * The default date format string which can be overriden for localization support.  The format must be
20838      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20839      */
20840     format : "m/d/y",
20841     /**
20842      * @cfg {String} altFormats
20843      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20844      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20845      */
20846     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20847     
20848     weekStart : 0,
20849     
20850     viewMode : '',
20851     
20852     minViewMode : '',
20853     
20854     todayHighlight : false,
20855     
20856     todayBtn: false,
20857     
20858     language: 'en',
20859     
20860     keyboardNavigation: true,
20861     
20862     calendarWeeks: false,
20863     
20864     startDate: -Infinity,
20865     
20866     endDate: Infinity,
20867     
20868     daysOfWeekDisabled: [],
20869     
20870     _events: [],
20871     
20872     singleMode : false,
20873     
20874     UTCDate: function()
20875     {
20876         return new Date(Date.UTC.apply(Date, arguments));
20877     },
20878     
20879     UTCToday: function()
20880     {
20881         var today = new Date();
20882         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20883     },
20884     
20885     getDate: function() {
20886             var d = this.getUTCDate();
20887             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20888     },
20889     
20890     getUTCDate: function() {
20891             return this.date;
20892     },
20893     
20894     setDate: function(d) {
20895             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20896     },
20897     
20898     setUTCDate: function(d) {
20899             this.date = d;
20900             this.setValue(this.formatDate(this.date));
20901     },
20902         
20903     onRender: function(ct, position)
20904     {
20905         
20906         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20907         
20908         this.language = this.language || 'en';
20909         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20910         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20911         
20912         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20913         this.format = this.format || 'm/d/y';
20914         this.isInline = false;
20915         this.isInput = true;
20916         this.component = this.el.select('.add-on', true).first() || false;
20917         this.component = (this.component && this.component.length === 0) ? false : this.component;
20918         this.hasInput = this.component && this.inputEl().length;
20919         
20920         if (typeof(this.minViewMode === 'string')) {
20921             switch (this.minViewMode) {
20922                 case 'months':
20923                     this.minViewMode = 1;
20924                     break;
20925                 case 'years':
20926                     this.minViewMode = 2;
20927                     break;
20928                 default:
20929                     this.minViewMode = 0;
20930                     break;
20931             }
20932         }
20933         
20934         if (typeof(this.viewMode === 'string')) {
20935             switch (this.viewMode) {
20936                 case 'months':
20937                     this.viewMode = 1;
20938                     break;
20939                 case 'years':
20940                     this.viewMode = 2;
20941                     break;
20942                 default:
20943                     this.viewMode = 0;
20944                     break;
20945             }
20946         }
20947                 
20948         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20949         
20950 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20951         
20952         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20953         
20954         this.picker().on('mousedown', this.onMousedown, this);
20955         this.picker().on('click', this.onClick, this);
20956         
20957         this.picker().addClass('datepicker-dropdown');
20958         
20959         this.startViewMode = this.viewMode;
20960         
20961         if(this.singleMode){
20962             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20963                 v.setVisibilityMode(Roo.Element.DISPLAY);
20964                 v.hide();
20965             });
20966             
20967             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20968                 v.setStyle('width', '189px');
20969             });
20970         }
20971         
20972         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20973             if(!this.calendarWeeks){
20974                 v.remove();
20975                 return;
20976             }
20977             
20978             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20979             v.attr('colspan', function(i, val){
20980                 return parseInt(val) + 1;
20981             });
20982         });
20983                         
20984         
20985         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20986         
20987         this.setStartDate(this.startDate);
20988         this.setEndDate(this.endDate);
20989         
20990         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20991         
20992         this.fillDow();
20993         this.fillMonths();
20994         this.update();
20995         this.showMode();
20996         
20997         if(this.isInline) {
20998             this.showPopup();
20999         }
21000     },
21001     
21002     picker : function()
21003     {
21004         return this.pickerEl;
21005 //        return this.el.select('.datepicker', true).first();
21006     },
21007     
21008     fillDow: function()
21009     {
21010         var dowCnt = this.weekStart;
21011         
21012         var dow = {
21013             tag: 'tr',
21014             cn: [
21015                 
21016             ]
21017         };
21018         
21019         if(this.calendarWeeks){
21020             dow.cn.push({
21021                 tag: 'th',
21022                 cls: 'cw',
21023                 html: '&nbsp;'
21024             })
21025         }
21026         
21027         while (dowCnt < this.weekStart + 7) {
21028             dow.cn.push({
21029                 tag: 'th',
21030                 cls: 'dow',
21031                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21032             });
21033         }
21034         
21035         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21036     },
21037     
21038     fillMonths: function()
21039     {    
21040         var i = 0;
21041         var months = this.picker().select('>.datepicker-months td', true).first();
21042         
21043         months.dom.innerHTML = '';
21044         
21045         while (i < 12) {
21046             var month = {
21047                 tag: 'span',
21048                 cls: 'month',
21049                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21050             };
21051             
21052             months.createChild(month);
21053         }
21054         
21055     },
21056     
21057     update: function()
21058     {
21059         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;
21060         
21061         if (this.date < this.startDate) {
21062             this.viewDate = new Date(this.startDate);
21063         } else if (this.date > this.endDate) {
21064             this.viewDate = new Date(this.endDate);
21065         } else {
21066             this.viewDate = new Date(this.date);
21067         }
21068         
21069         this.fill();
21070     },
21071     
21072     fill: function() 
21073     {
21074         var d = new Date(this.viewDate),
21075                 year = d.getUTCFullYear(),
21076                 month = d.getUTCMonth(),
21077                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21078                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21079                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21080                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21081                 currentDate = this.date && this.date.valueOf(),
21082                 today = this.UTCToday();
21083         
21084         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21085         
21086 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21087         
21088 //        this.picker.select('>tfoot th.today').
21089 //                                              .text(dates[this.language].today)
21090 //                                              .toggle(this.todayBtn !== false);
21091     
21092         this.updateNavArrows();
21093         this.fillMonths();
21094                                                 
21095         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21096         
21097         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21098          
21099         prevMonth.setUTCDate(day);
21100         
21101         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21102         
21103         var nextMonth = new Date(prevMonth);
21104         
21105         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21106         
21107         nextMonth = nextMonth.valueOf();
21108         
21109         var fillMonths = false;
21110         
21111         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21112         
21113         while(prevMonth.valueOf() <= nextMonth) {
21114             var clsName = '';
21115             
21116             if (prevMonth.getUTCDay() === this.weekStart) {
21117                 if(fillMonths){
21118                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21119                 }
21120                     
21121                 fillMonths = {
21122                     tag: 'tr',
21123                     cn: []
21124                 };
21125                 
21126                 if(this.calendarWeeks){
21127                     // ISO 8601: First week contains first thursday.
21128                     // ISO also states week starts on Monday, but we can be more abstract here.
21129                     var
21130                     // Start of current week: based on weekstart/current date
21131                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21132                     // Thursday of this week
21133                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21134                     // First Thursday of year, year from thursday
21135                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21136                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21137                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21138                     
21139                     fillMonths.cn.push({
21140                         tag: 'td',
21141                         cls: 'cw',
21142                         html: calWeek
21143                     });
21144                 }
21145             }
21146             
21147             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21148                 clsName += ' old';
21149             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21150                 clsName += ' new';
21151             }
21152             if (this.todayHighlight &&
21153                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21154                 prevMonth.getUTCMonth() == today.getMonth() &&
21155                 prevMonth.getUTCDate() == today.getDate()) {
21156                 clsName += ' today';
21157             }
21158             
21159             if (currentDate && prevMonth.valueOf() === currentDate) {
21160                 clsName += ' active';
21161             }
21162             
21163             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21164                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21165                     clsName += ' disabled';
21166             }
21167             
21168             fillMonths.cn.push({
21169                 tag: 'td',
21170                 cls: 'day ' + clsName,
21171                 html: prevMonth.getDate()
21172             });
21173             
21174             prevMonth.setDate(prevMonth.getDate()+1);
21175         }
21176           
21177         var currentYear = this.date && this.date.getUTCFullYear();
21178         var currentMonth = this.date && this.date.getUTCMonth();
21179         
21180         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21181         
21182         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21183             v.removeClass('active');
21184             
21185             if(currentYear === year && k === currentMonth){
21186                 v.addClass('active');
21187             }
21188             
21189             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21190                 v.addClass('disabled');
21191             }
21192             
21193         });
21194         
21195         
21196         year = parseInt(year/10, 10) * 10;
21197         
21198         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21199         
21200         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21201         
21202         year -= 1;
21203         for (var i = -1; i < 11; i++) {
21204             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21205                 tag: 'span',
21206                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21207                 html: year
21208             });
21209             
21210             year += 1;
21211         }
21212     },
21213     
21214     showMode: function(dir) 
21215     {
21216         if (dir) {
21217             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21218         }
21219         
21220         Roo.each(this.picker().select('>div',true).elements, function(v){
21221             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21222             v.hide();
21223         });
21224         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21225     },
21226     
21227     place: function()
21228     {
21229         if(this.isInline) {
21230             return;
21231         }
21232         
21233         this.picker().removeClass(['bottom', 'top']);
21234         
21235         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21236             /*
21237              * place to the top of element!
21238              *
21239              */
21240             
21241             this.picker().addClass('top');
21242             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21243             
21244             return;
21245         }
21246         
21247         this.picker().addClass('bottom');
21248         
21249         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21250     },
21251     
21252     parseDate : function(value)
21253     {
21254         if(!value || value instanceof Date){
21255             return value;
21256         }
21257         var v = Date.parseDate(value, this.format);
21258         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21259             v = Date.parseDate(value, 'Y-m-d');
21260         }
21261         if(!v && this.altFormats){
21262             if(!this.altFormatsArray){
21263                 this.altFormatsArray = this.altFormats.split("|");
21264             }
21265             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21266                 v = Date.parseDate(value, this.altFormatsArray[i]);
21267             }
21268         }
21269         return v;
21270     },
21271     
21272     formatDate : function(date, fmt)
21273     {   
21274         return (!date || !(date instanceof Date)) ?
21275         date : date.dateFormat(fmt || this.format);
21276     },
21277     
21278     onFocus : function()
21279     {
21280         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21281         this.showPopup();
21282     },
21283     
21284     onBlur : function()
21285     {
21286         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21287         
21288         var d = this.inputEl().getValue();
21289         
21290         this.setValue(d);
21291                 
21292         this.hidePopup();
21293     },
21294     
21295     showPopup : function()
21296     {
21297         this.picker().show();
21298         this.update();
21299         this.place();
21300         
21301         this.fireEvent('showpopup', this, this.date);
21302     },
21303     
21304     hidePopup : function()
21305     {
21306         if(this.isInline) {
21307             return;
21308         }
21309         this.picker().hide();
21310         this.viewMode = this.startViewMode;
21311         this.showMode();
21312         
21313         this.fireEvent('hidepopup', this, this.date);
21314         
21315     },
21316     
21317     onMousedown: function(e)
21318     {
21319         e.stopPropagation();
21320         e.preventDefault();
21321     },
21322     
21323     keyup: function(e)
21324     {
21325         Roo.bootstrap.DateField.superclass.keyup.call(this);
21326         this.update();
21327     },
21328
21329     setValue: function(v)
21330     {
21331         if(this.fireEvent('beforeselect', this, v) !== false){
21332             var d = new Date(this.parseDate(v) ).clearTime();
21333         
21334             if(isNaN(d.getTime())){
21335                 this.date = this.viewDate = '';
21336                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21337                 return;
21338             }
21339
21340             v = this.formatDate(d);
21341
21342             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21343
21344             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21345
21346             this.update();
21347
21348             this.fireEvent('select', this, this.date);
21349         }
21350     },
21351     
21352     getValue: function()
21353     {
21354         return this.formatDate(this.date);
21355     },
21356     
21357     fireKey: function(e)
21358     {
21359         if (!this.picker().isVisible()){
21360             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21361                 this.showPopup();
21362             }
21363             return;
21364         }
21365         
21366         var dateChanged = false,
21367         dir, day, month,
21368         newDate, newViewDate;
21369         
21370         switch(e.keyCode){
21371             case 27: // escape
21372                 this.hidePopup();
21373                 e.preventDefault();
21374                 break;
21375             case 37: // left
21376             case 39: // right
21377                 if (!this.keyboardNavigation) {
21378                     break;
21379                 }
21380                 dir = e.keyCode == 37 ? -1 : 1;
21381                 
21382                 if (e.ctrlKey){
21383                     newDate = this.moveYear(this.date, dir);
21384                     newViewDate = this.moveYear(this.viewDate, dir);
21385                 } else if (e.shiftKey){
21386                     newDate = this.moveMonth(this.date, dir);
21387                     newViewDate = this.moveMonth(this.viewDate, dir);
21388                 } else {
21389                     newDate = new Date(this.date);
21390                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21391                     newViewDate = new Date(this.viewDate);
21392                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21393                 }
21394                 if (this.dateWithinRange(newDate)){
21395                     this.date = newDate;
21396                     this.viewDate = newViewDate;
21397                     this.setValue(this.formatDate(this.date));
21398 //                    this.update();
21399                     e.preventDefault();
21400                     dateChanged = true;
21401                 }
21402                 break;
21403             case 38: // up
21404             case 40: // down
21405                 if (!this.keyboardNavigation) {
21406                     break;
21407                 }
21408                 dir = e.keyCode == 38 ? -1 : 1;
21409                 if (e.ctrlKey){
21410                     newDate = this.moveYear(this.date, dir);
21411                     newViewDate = this.moveYear(this.viewDate, dir);
21412                 } else if (e.shiftKey){
21413                     newDate = this.moveMonth(this.date, dir);
21414                     newViewDate = this.moveMonth(this.viewDate, dir);
21415                 } else {
21416                     newDate = new Date(this.date);
21417                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21418                     newViewDate = new Date(this.viewDate);
21419                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21420                 }
21421                 if (this.dateWithinRange(newDate)){
21422                     this.date = newDate;
21423                     this.viewDate = newViewDate;
21424                     this.setValue(this.formatDate(this.date));
21425 //                    this.update();
21426                     e.preventDefault();
21427                     dateChanged = true;
21428                 }
21429                 break;
21430             case 13: // enter
21431                 this.setValue(this.formatDate(this.date));
21432                 this.hidePopup();
21433                 e.preventDefault();
21434                 break;
21435             case 9: // tab
21436                 this.setValue(this.formatDate(this.date));
21437                 this.hidePopup();
21438                 break;
21439             case 16: // shift
21440             case 17: // ctrl
21441             case 18: // alt
21442                 break;
21443             default :
21444                 this.hidePopup();
21445                 
21446         }
21447     },
21448     
21449     
21450     onClick: function(e) 
21451     {
21452         e.stopPropagation();
21453         e.preventDefault();
21454         
21455         var target = e.getTarget();
21456         
21457         if(target.nodeName.toLowerCase() === 'i'){
21458             target = Roo.get(target).dom.parentNode;
21459         }
21460         
21461         var nodeName = target.nodeName;
21462         var className = target.className;
21463         var html = target.innerHTML;
21464         //Roo.log(nodeName);
21465         
21466         switch(nodeName.toLowerCase()) {
21467             case 'th':
21468                 switch(className) {
21469                     case 'switch':
21470                         this.showMode(1);
21471                         break;
21472                     case 'prev':
21473                     case 'next':
21474                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21475                         switch(this.viewMode){
21476                                 case 0:
21477                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21478                                         break;
21479                                 case 1:
21480                                 case 2:
21481                                         this.viewDate = this.moveYear(this.viewDate, dir);
21482                                         break;
21483                         }
21484                         this.fill();
21485                         break;
21486                     case 'today':
21487                         var date = new Date();
21488                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21489 //                        this.fill()
21490                         this.setValue(this.formatDate(this.date));
21491                         
21492                         this.hidePopup();
21493                         break;
21494                 }
21495                 break;
21496             case 'span':
21497                 if (className.indexOf('disabled') < 0) {
21498                     this.viewDate.setUTCDate(1);
21499                     if (className.indexOf('month') > -1) {
21500                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21501                     } else {
21502                         var year = parseInt(html, 10) || 0;
21503                         this.viewDate.setUTCFullYear(year);
21504                         
21505                     }
21506                     
21507                     if(this.singleMode){
21508                         this.setValue(this.formatDate(this.viewDate));
21509                         this.hidePopup();
21510                         return;
21511                     }
21512                     
21513                     this.showMode(-1);
21514                     this.fill();
21515                 }
21516                 break;
21517                 
21518             case 'td':
21519                 //Roo.log(className);
21520                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21521                     var day = parseInt(html, 10) || 1;
21522                     var year = this.viewDate.getUTCFullYear(),
21523                         month = this.viewDate.getUTCMonth();
21524
21525                     if (className.indexOf('old') > -1) {
21526                         if(month === 0 ){
21527                             month = 11;
21528                             year -= 1;
21529                         }else{
21530                             month -= 1;
21531                         }
21532                     } else if (className.indexOf('new') > -1) {
21533                         if (month == 11) {
21534                             month = 0;
21535                             year += 1;
21536                         } else {
21537                             month += 1;
21538                         }
21539                     }
21540                     //Roo.log([year,month,day]);
21541                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21542                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21543 //                    this.fill();
21544                     //Roo.log(this.formatDate(this.date));
21545                     this.setValue(this.formatDate(this.date));
21546                     this.hidePopup();
21547                 }
21548                 break;
21549         }
21550     },
21551     
21552     setStartDate: function(startDate)
21553     {
21554         this.startDate = startDate || -Infinity;
21555         if (this.startDate !== -Infinity) {
21556             this.startDate = this.parseDate(this.startDate);
21557         }
21558         this.update();
21559         this.updateNavArrows();
21560     },
21561
21562     setEndDate: function(endDate)
21563     {
21564         this.endDate = endDate || Infinity;
21565         if (this.endDate !== Infinity) {
21566             this.endDate = this.parseDate(this.endDate);
21567         }
21568         this.update();
21569         this.updateNavArrows();
21570     },
21571     
21572     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21573     {
21574         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21575         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21576             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21577         }
21578         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21579             return parseInt(d, 10);
21580         });
21581         this.update();
21582         this.updateNavArrows();
21583     },
21584     
21585     updateNavArrows: function() 
21586     {
21587         if(this.singleMode){
21588             return;
21589         }
21590         
21591         var d = new Date(this.viewDate),
21592         year = d.getUTCFullYear(),
21593         month = d.getUTCMonth();
21594         
21595         Roo.each(this.picker().select('.prev', true).elements, function(v){
21596             v.show();
21597             switch (this.viewMode) {
21598                 case 0:
21599
21600                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21601                         v.hide();
21602                     }
21603                     break;
21604                 case 1:
21605                 case 2:
21606                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21607                         v.hide();
21608                     }
21609                     break;
21610             }
21611         });
21612         
21613         Roo.each(this.picker().select('.next', true).elements, function(v){
21614             v.show();
21615             switch (this.viewMode) {
21616                 case 0:
21617
21618                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21619                         v.hide();
21620                     }
21621                     break;
21622                 case 1:
21623                 case 2:
21624                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21625                         v.hide();
21626                     }
21627                     break;
21628             }
21629         })
21630     },
21631     
21632     moveMonth: function(date, dir)
21633     {
21634         if (!dir) {
21635             return date;
21636         }
21637         var new_date = new Date(date.valueOf()),
21638         day = new_date.getUTCDate(),
21639         month = new_date.getUTCMonth(),
21640         mag = Math.abs(dir),
21641         new_month, test;
21642         dir = dir > 0 ? 1 : -1;
21643         if (mag == 1){
21644             test = dir == -1
21645             // If going back one month, make sure month is not current month
21646             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21647             ? function(){
21648                 return new_date.getUTCMonth() == month;
21649             }
21650             // If going forward one month, make sure month is as expected
21651             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21652             : function(){
21653                 return new_date.getUTCMonth() != new_month;
21654             };
21655             new_month = month + dir;
21656             new_date.setUTCMonth(new_month);
21657             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21658             if (new_month < 0 || new_month > 11) {
21659                 new_month = (new_month + 12) % 12;
21660             }
21661         } else {
21662             // For magnitudes >1, move one month at a time...
21663             for (var i=0; i<mag; i++) {
21664                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21665                 new_date = this.moveMonth(new_date, dir);
21666             }
21667             // ...then reset the day, keeping it in the new month
21668             new_month = new_date.getUTCMonth();
21669             new_date.setUTCDate(day);
21670             test = function(){
21671                 return new_month != new_date.getUTCMonth();
21672             };
21673         }
21674         // Common date-resetting loop -- if date is beyond end of month, make it
21675         // end of month
21676         while (test()){
21677             new_date.setUTCDate(--day);
21678             new_date.setUTCMonth(new_month);
21679         }
21680         return new_date;
21681     },
21682
21683     moveYear: function(date, dir)
21684     {
21685         return this.moveMonth(date, dir*12);
21686     },
21687
21688     dateWithinRange: function(date)
21689     {
21690         return date >= this.startDate && date <= this.endDate;
21691     },
21692
21693     
21694     remove: function() 
21695     {
21696         this.picker().remove();
21697     },
21698     
21699     validateValue : function(value)
21700     {
21701         if(this.getVisibilityEl().hasClass('hidden')){
21702             return true;
21703         }
21704         
21705         if(value.length < 1)  {
21706             if(this.allowBlank){
21707                 return true;
21708             }
21709             return false;
21710         }
21711         
21712         if(value.length < this.minLength){
21713             return false;
21714         }
21715         if(value.length > this.maxLength){
21716             return false;
21717         }
21718         if(this.vtype){
21719             var vt = Roo.form.VTypes;
21720             if(!vt[this.vtype](value, this)){
21721                 return false;
21722             }
21723         }
21724         if(typeof this.validator == "function"){
21725             var msg = this.validator(value);
21726             if(msg !== true){
21727                 return false;
21728             }
21729         }
21730         
21731         if(this.regex && !this.regex.test(value)){
21732             return false;
21733         }
21734         
21735         if(typeof(this.parseDate(value)) == 'undefined'){
21736             return false;
21737         }
21738         
21739         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21740             return false;
21741         }      
21742         
21743         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21744             return false;
21745         } 
21746         
21747         
21748         return true;
21749     },
21750     
21751     reset : function()
21752     {
21753         this.date = this.viewDate = '';
21754         
21755         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21756     }
21757    
21758 });
21759
21760 Roo.apply(Roo.bootstrap.DateField,  {
21761     
21762     head : {
21763         tag: 'thead',
21764         cn: [
21765         {
21766             tag: 'tr',
21767             cn: [
21768             {
21769                 tag: 'th',
21770                 cls: 'prev',
21771                 html: '<i class="fa fa-arrow-left"/>'
21772             },
21773             {
21774                 tag: 'th',
21775                 cls: 'switch',
21776                 colspan: '5'
21777             },
21778             {
21779                 tag: 'th',
21780                 cls: 'next',
21781                 html: '<i class="fa fa-arrow-right"/>'
21782             }
21783
21784             ]
21785         }
21786         ]
21787     },
21788     
21789     content : {
21790         tag: 'tbody',
21791         cn: [
21792         {
21793             tag: 'tr',
21794             cn: [
21795             {
21796                 tag: 'td',
21797                 colspan: '7'
21798             }
21799             ]
21800         }
21801         ]
21802     },
21803     
21804     footer : {
21805         tag: 'tfoot',
21806         cn: [
21807         {
21808             tag: 'tr',
21809             cn: [
21810             {
21811                 tag: 'th',
21812                 colspan: '7',
21813                 cls: 'today'
21814             }
21815                     
21816             ]
21817         }
21818         ]
21819     },
21820     
21821     dates:{
21822         en: {
21823             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21824             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21825             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21826             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21827             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21828             today: "Today"
21829         }
21830     },
21831     
21832     modes: [
21833     {
21834         clsName: 'days',
21835         navFnc: 'Month',
21836         navStep: 1
21837     },
21838     {
21839         clsName: 'months',
21840         navFnc: 'FullYear',
21841         navStep: 1
21842     },
21843     {
21844         clsName: 'years',
21845         navFnc: 'FullYear',
21846         navStep: 10
21847     }]
21848 });
21849
21850 Roo.apply(Roo.bootstrap.DateField,  {
21851   
21852     template : {
21853         tag: 'div',
21854         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21855         cn: [
21856         {
21857             tag: 'div',
21858             cls: 'datepicker-days',
21859             cn: [
21860             {
21861                 tag: 'table',
21862                 cls: 'table-condensed',
21863                 cn:[
21864                 Roo.bootstrap.DateField.head,
21865                 {
21866                     tag: 'tbody'
21867                 },
21868                 Roo.bootstrap.DateField.footer
21869                 ]
21870             }
21871             ]
21872         },
21873         {
21874             tag: 'div',
21875             cls: 'datepicker-months',
21876             cn: [
21877             {
21878                 tag: 'table',
21879                 cls: 'table-condensed',
21880                 cn:[
21881                 Roo.bootstrap.DateField.head,
21882                 Roo.bootstrap.DateField.content,
21883                 Roo.bootstrap.DateField.footer
21884                 ]
21885             }
21886             ]
21887         },
21888         {
21889             tag: 'div',
21890             cls: 'datepicker-years',
21891             cn: [
21892             {
21893                 tag: 'table',
21894                 cls: 'table-condensed',
21895                 cn:[
21896                 Roo.bootstrap.DateField.head,
21897                 Roo.bootstrap.DateField.content,
21898                 Roo.bootstrap.DateField.footer
21899                 ]
21900             }
21901             ]
21902         }
21903         ]
21904     }
21905 });
21906
21907  
21908
21909  /*
21910  * - LGPL
21911  *
21912  * TimeField
21913  * 
21914  */
21915
21916 /**
21917  * @class Roo.bootstrap.TimeField
21918  * @extends Roo.bootstrap.Input
21919  * Bootstrap DateField class
21920  * 
21921  * 
21922  * @constructor
21923  * Create a new TimeField
21924  * @param {Object} config The config object
21925  */
21926
21927 Roo.bootstrap.TimeField = function(config){
21928     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21929     this.addEvents({
21930             /**
21931              * @event show
21932              * Fires when this field show.
21933              * @param {Roo.bootstrap.DateField} thisthis
21934              * @param {Mixed} date The date value
21935              */
21936             show : true,
21937             /**
21938              * @event show
21939              * Fires when this field hide.
21940              * @param {Roo.bootstrap.DateField} this
21941              * @param {Mixed} date The date value
21942              */
21943             hide : true,
21944             /**
21945              * @event select
21946              * Fires when select a date.
21947              * @param {Roo.bootstrap.DateField} this
21948              * @param {Mixed} date The date value
21949              */
21950             select : true
21951         });
21952 };
21953
21954 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21955     
21956     /**
21957      * @cfg {String} format
21958      * The default time format string which can be overriden for localization support.  The format must be
21959      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21960      */
21961     format : "H:i",
21962        
21963     onRender: function(ct, position)
21964     {
21965         
21966         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21967                 
21968         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21969         
21970         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21971         
21972         this.pop = this.picker().select('>.datepicker-time',true).first();
21973         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21974         
21975         this.picker().on('mousedown', this.onMousedown, this);
21976         this.picker().on('click', this.onClick, this);
21977         
21978         this.picker().addClass('datepicker-dropdown');
21979     
21980         this.fillTime();
21981         this.update();
21982             
21983         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21984         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21985         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21986         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21987         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21988         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21989
21990     },
21991     
21992     fireKey: function(e){
21993         if (!this.picker().isVisible()){
21994             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21995                 this.show();
21996             }
21997             return;
21998         }
21999
22000         e.preventDefault();
22001         
22002         switch(e.keyCode){
22003             case 27: // escape
22004                 this.hide();
22005                 break;
22006             case 37: // left
22007             case 39: // right
22008                 this.onTogglePeriod();
22009                 break;
22010             case 38: // up
22011                 this.onIncrementMinutes();
22012                 break;
22013             case 40: // down
22014                 this.onDecrementMinutes();
22015                 break;
22016             case 13: // enter
22017             case 9: // tab
22018                 this.setTime();
22019                 break;
22020         }
22021     },
22022     
22023     onClick: function(e) {
22024         e.stopPropagation();
22025         e.preventDefault();
22026     },
22027     
22028     picker : function()
22029     {
22030         return this.el.select('.datepicker', true).first();
22031     },
22032     
22033     fillTime: function()
22034     {    
22035         var time = this.pop.select('tbody', true).first();
22036         
22037         time.dom.innerHTML = '';
22038         
22039         time.createChild({
22040             tag: 'tr',
22041             cn: [
22042                 {
22043                     tag: 'td',
22044                     cn: [
22045                         {
22046                             tag: 'a',
22047                             href: '#',
22048                             cls: 'btn',
22049                             cn: [
22050                                 {
22051                                     tag: 'span',
22052                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22053                                 }
22054                             ]
22055                         } 
22056                     ]
22057                 },
22058                 {
22059                     tag: 'td',
22060                     cls: 'separator'
22061                 },
22062                 {
22063                     tag: 'td',
22064                     cn: [
22065                         {
22066                             tag: 'a',
22067                             href: '#',
22068                             cls: 'btn',
22069                             cn: [
22070                                 {
22071                                     tag: 'span',
22072                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22073                                 }
22074                             ]
22075                         }
22076                     ]
22077                 },
22078                 {
22079                     tag: 'td',
22080                     cls: 'separator'
22081                 }
22082             ]
22083         });
22084         
22085         time.createChild({
22086             tag: 'tr',
22087             cn: [
22088                 {
22089                     tag: 'td',
22090                     cn: [
22091                         {
22092                             tag: 'span',
22093                             cls: 'timepicker-hour',
22094                             html: '00'
22095                         }  
22096                     ]
22097                 },
22098                 {
22099                     tag: 'td',
22100                     cls: 'separator',
22101                     html: ':'
22102                 },
22103                 {
22104                     tag: 'td',
22105                     cn: [
22106                         {
22107                             tag: 'span',
22108                             cls: 'timepicker-minute',
22109                             html: '00'
22110                         }  
22111                     ]
22112                 },
22113                 {
22114                     tag: 'td',
22115                     cls: 'separator'
22116                 },
22117                 {
22118                     tag: 'td',
22119                     cn: [
22120                         {
22121                             tag: 'button',
22122                             type: 'button',
22123                             cls: 'btn btn-primary period',
22124                             html: 'AM'
22125                             
22126                         }
22127                     ]
22128                 }
22129             ]
22130         });
22131         
22132         time.createChild({
22133             tag: 'tr',
22134             cn: [
22135                 {
22136                     tag: 'td',
22137                     cn: [
22138                         {
22139                             tag: 'a',
22140                             href: '#',
22141                             cls: 'btn',
22142                             cn: [
22143                                 {
22144                                     tag: 'span',
22145                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22146                                 }
22147                             ]
22148                         }
22149                     ]
22150                 },
22151                 {
22152                     tag: 'td',
22153                     cls: 'separator'
22154                 },
22155                 {
22156                     tag: 'td',
22157                     cn: [
22158                         {
22159                             tag: 'a',
22160                             href: '#',
22161                             cls: 'btn',
22162                             cn: [
22163                                 {
22164                                     tag: 'span',
22165                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22166                                 }
22167                             ]
22168                         }
22169                     ]
22170                 },
22171                 {
22172                     tag: 'td',
22173                     cls: 'separator'
22174                 }
22175             ]
22176         });
22177         
22178     },
22179     
22180     update: function()
22181     {
22182         
22183         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22184         
22185         this.fill();
22186     },
22187     
22188     fill: function() 
22189     {
22190         var hours = this.time.getHours();
22191         var minutes = this.time.getMinutes();
22192         var period = 'AM';
22193         
22194         if(hours > 11){
22195             period = 'PM';
22196         }
22197         
22198         if(hours == 0){
22199             hours = 12;
22200         }
22201         
22202         
22203         if(hours > 12){
22204             hours = hours - 12;
22205         }
22206         
22207         if(hours < 10){
22208             hours = '0' + hours;
22209         }
22210         
22211         if(minutes < 10){
22212             minutes = '0' + minutes;
22213         }
22214         
22215         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22216         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22217         this.pop.select('button', true).first().dom.innerHTML = period;
22218         
22219     },
22220     
22221     place: function()
22222     {   
22223         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22224         
22225         var cls = ['bottom'];
22226         
22227         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22228             cls.pop();
22229             cls.push('top');
22230         }
22231         
22232         cls.push('right');
22233         
22234         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22235             cls.pop();
22236             cls.push('left');
22237         }
22238         
22239         this.picker().addClass(cls.join('-'));
22240         
22241         var _this = this;
22242         
22243         Roo.each(cls, function(c){
22244             if(c == 'bottom'){
22245                 _this.picker().setTop(_this.inputEl().getHeight());
22246                 return;
22247             }
22248             if(c == 'top'){
22249                 _this.picker().setTop(0 - _this.picker().getHeight());
22250                 return;
22251             }
22252             
22253             if(c == 'left'){
22254                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22255                 return;
22256             }
22257             if(c == 'right'){
22258                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22259                 return;
22260             }
22261         });
22262         
22263     },
22264   
22265     onFocus : function()
22266     {
22267         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22268         this.show();
22269     },
22270     
22271     onBlur : function()
22272     {
22273         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22274         this.hide();
22275     },
22276     
22277     show : function()
22278     {
22279         this.picker().show();
22280         this.pop.show();
22281         this.update();
22282         this.place();
22283         
22284         this.fireEvent('show', this, this.date);
22285     },
22286     
22287     hide : function()
22288     {
22289         this.picker().hide();
22290         this.pop.hide();
22291         
22292         this.fireEvent('hide', this, this.date);
22293     },
22294     
22295     setTime : function()
22296     {
22297         this.hide();
22298         this.setValue(this.time.format(this.format));
22299         
22300         this.fireEvent('select', this, this.date);
22301         
22302         
22303     },
22304     
22305     onMousedown: function(e){
22306         e.stopPropagation();
22307         e.preventDefault();
22308     },
22309     
22310     onIncrementHours: function()
22311     {
22312         Roo.log('onIncrementHours');
22313         this.time = this.time.add(Date.HOUR, 1);
22314         this.update();
22315         
22316     },
22317     
22318     onDecrementHours: function()
22319     {
22320         Roo.log('onDecrementHours');
22321         this.time = this.time.add(Date.HOUR, -1);
22322         this.update();
22323     },
22324     
22325     onIncrementMinutes: function()
22326     {
22327         Roo.log('onIncrementMinutes');
22328         this.time = this.time.add(Date.MINUTE, 1);
22329         this.update();
22330     },
22331     
22332     onDecrementMinutes: function()
22333     {
22334         Roo.log('onDecrementMinutes');
22335         this.time = this.time.add(Date.MINUTE, -1);
22336         this.update();
22337     },
22338     
22339     onTogglePeriod: function()
22340     {
22341         Roo.log('onTogglePeriod');
22342         this.time = this.time.add(Date.HOUR, 12);
22343         this.update();
22344     }
22345     
22346    
22347 });
22348
22349 Roo.apply(Roo.bootstrap.TimeField,  {
22350     
22351     content : {
22352         tag: 'tbody',
22353         cn: [
22354             {
22355                 tag: 'tr',
22356                 cn: [
22357                 {
22358                     tag: 'td',
22359                     colspan: '7'
22360                 }
22361                 ]
22362             }
22363         ]
22364     },
22365     
22366     footer : {
22367         tag: 'tfoot',
22368         cn: [
22369             {
22370                 tag: 'tr',
22371                 cn: [
22372                 {
22373                     tag: 'th',
22374                     colspan: '7',
22375                     cls: '',
22376                     cn: [
22377                         {
22378                             tag: 'button',
22379                             cls: 'btn btn-info ok',
22380                             html: 'OK'
22381                         }
22382                     ]
22383                 }
22384
22385                 ]
22386             }
22387         ]
22388     }
22389 });
22390
22391 Roo.apply(Roo.bootstrap.TimeField,  {
22392   
22393     template : {
22394         tag: 'div',
22395         cls: 'datepicker dropdown-menu',
22396         cn: [
22397             {
22398                 tag: 'div',
22399                 cls: 'datepicker-time',
22400                 cn: [
22401                 {
22402                     tag: 'table',
22403                     cls: 'table-condensed',
22404                     cn:[
22405                     Roo.bootstrap.TimeField.content,
22406                     Roo.bootstrap.TimeField.footer
22407                     ]
22408                 }
22409                 ]
22410             }
22411         ]
22412     }
22413 });
22414
22415  
22416
22417  /*
22418  * - LGPL
22419  *
22420  * MonthField
22421  * 
22422  */
22423
22424 /**
22425  * @class Roo.bootstrap.MonthField
22426  * @extends Roo.bootstrap.Input
22427  * Bootstrap MonthField class
22428  * 
22429  * @cfg {String} language default en
22430  * 
22431  * @constructor
22432  * Create a new MonthField
22433  * @param {Object} config The config object
22434  */
22435
22436 Roo.bootstrap.MonthField = function(config){
22437     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22438     
22439     this.addEvents({
22440         /**
22441          * @event show
22442          * Fires when this field show.
22443          * @param {Roo.bootstrap.MonthField} this
22444          * @param {Mixed} date The date value
22445          */
22446         show : true,
22447         /**
22448          * @event show
22449          * Fires when this field hide.
22450          * @param {Roo.bootstrap.MonthField} this
22451          * @param {Mixed} date The date value
22452          */
22453         hide : true,
22454         /**
22455          * @event select
22456          * Fires when select a date.
22457          * @param {Roo.bootstrap.MonthField} this
22458          * @param {String} oldvalue The old value
22459          * @param {String} newvalue The new value
22460          */
22461         select : true
22462     });
22463 };
22464
22465 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22466     
22467     onRender: function(ct, position)
22468     {
22469         
22470         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22471         
22472         this.language = this.language || 'en';
22473         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22474         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22475         
22476         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22477         this.isInline = false;
22478         this.isInput = true;
22479         this.component = this.el.select('.add-on', true).first() || false;
22480         this.component = (this.component && this.component.length === 0) ? false : this.component;
22481         this.hasInput = this.component && this.inputEL().length;
22482         
22483         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22484         
22485         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22486         
22487         this.picker().on('mousedown', this.onMousedown, this);
22488         this.picker().on('click', this.onClick, this);
22489         
22490         this.picker().addClass('datepicker-dropdown');
22491         
22492         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22493             v.setStyle('width', '189px');
22494         });
22495         
22496         this.fillMonths();
22497         
22498         this.update();
22499         
22500         if(this.isInline) {
22501             this.show();
22502         }
22503         
22504     },
22505     
22506     setValue: function(v, suppressEvent)
22507     {   
22508         var o = this.getValue();
22509         
22510         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22511         
22512         this.update();
22513
22514         if(suppressEvent !== true){
22515             this.fireEvent('select', this, o, v);
22516         }
22517         
22518     },
22519     
22520     getValue: function()
22521     {
22522         return this.value;
22523     },
22524     
22525     onClick: function(e) 
22526     {
22527         e.stopPropagation();
22528         e.preventDefault();
22529         
22530         var target = e.getTarget();
22531         
22532         if(target.nodeName.toLowerCase() === 'i'){
22533             target = Roo.get(target).dom.parentNode;
22534         }
22535         
22536         var nodeName = target.nodeName;
22537         var className = target.className;
22538         var html = target.innerHTML;
22539         
22540         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22541             return;
22542         }
22543         
22544         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22545         
22546         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22547         
22548         this.hide();
22549                         
22550     },
22551     
22552     picker : function()
22553     {
22554         return this.pickerEl;
22555     },
22556     
22557     fillMonths: function()
22558     {    
22559         var i = 0;
22560         var months = this.picker().select('>.datepicker-months td', true).first();
22561         
22562         months.dom.innerHTML = '';
22563         
22564         while (i < 12) {
22565             var month = {
22566                 tag: 'span',
22567                 cls: 'month',
22568                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22569             };
22570             
22571             months.createChild(month);
22572         }
22573         
22574     },
22575     
22576     update: function()
22577     {
22578         var _this = this;
22579         
22580         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22581             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22582         }
22583         
22584         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22585             e.removeClass('active');
22586             
22587             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22588                 e.addClass('active');
22589             }
22590         })
22591     },
22592     
22593     place: function()
22594     {
22595         if(this.isInline) {
22596             return;
22597         }
22598         
22599         this.picker().removeClass(['bottom', 'top']);
22600         
22601         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22602             /*
22603              * place to the top of element!
22604              *
22605              */
22606             
22607             this.picker().addClass('top');
22608             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22609             
22610             return;
22611         }
22612         
22613         this.picker().addClass('bottom');
22614         
22615         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22616     },
22617     
22618     onFocus : function()
22619     {
22620         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22621         this.show();
22622     },
22623     
22624     onBlur : function()
22625     {
22626         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22627         
22628         var d = this.inputEl().getValue();
22629         
22630         this.setValue(d);
22631                 
22632         this.hide();
22633     },
22634     
22635     show : function()
22636     {
22637         this.picker().show();
22638         this.picker().select('>.datepicker-months', true).first().show();
22639         this.update();
22640         this.place();
22641         
22642         this.fireEvent('show', this, this.date);
22643     },
22644     
22645     hide : function()
22646     {
22647         if(this.isInline) {
22648             return;
22649         }
22650         this.picker().hide();
22651         this.fireEvent('hide', this, this.date);
22652         
22653     },
22654     
22655     onMousedown: function(e)
22656     {
22657         e.stopPropagation();
22658         e.preventDefault();
22659     },
22660     
22661     keyup: function(e)
22662     {
22663         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22664         this.update();
22665     },
22666
22667     fireKey: function(e)
22668     {
22669         if (!this.picker().isVisible()){
22670             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22671                 this.show();
22672             }
22673             return;
22674         }
22675         
22676         var dir;
22677         
22678         switch(e.keyCode){
22679             case 27: // escape
22680                 this.hide();
22681                 e.preventDefault();
22682                 break;
22683             case 37: // left
22684             case 39: // right
22685                 dir = e.keyCode == 37 ? -1 : 1;
22686                 
22687                 this.vIndex = this.vIndex + dir;
22688                 
22689                 if(this.vIndex < 0){
22690                     this.vIndex = 0;
22691                 }
22692                 
22693                 if(this.vIndex > 11){
22694                     this.vIndex = 11;
22695                 }
22696                 
22697                 if(isNaN(this.vIndex)){
22698                     this.vIndex = 0;
22699                 }
22700                 
22701                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22702                 
22703                 break;
22704             case 38: // up
22705             case 40: // down
22706                 
22707                 dir = e.keyCode == 38 ? -1 : 1;
22708                 
22709                 this.vIndex = this.vIndex + dir * 4;
22710                 
22711                 if(this.vIndex < 0){
22712                     this.vIndex = 0;
22713                 }
22714                 
22715                 if(this.vIndex > 11){
22716                     this.vIndex = 11;
22717                 }
22718                 
22719                 if(isNaN(this.vIndex)){
22720                     this.vIndex = 0;
22721                 }
22722                 
22723                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22724                 break;
22725                 
22726             case 13: // enter
22727                 
22728                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22729                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22730                 }
22731                 
22732                 this.hide();
22733                 e.preventDefault();
22734                 break;
22735             case 9: // tab
22736                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22737                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22738                 }
22739                 this.hide();
22740                 break;
22741             case 16: // shift
22742             case 17: // ctrl
22743             case 18: // alt
22744                 break;
22745             default :
22746                 this.hide();
22747                 
22748         }
22749     },
22750     
22751     remove: function() 
22752     {
22753         this.picker().remove();
22754     }
22755    
22756 });
22757
22758 Roo.apply(Roo.bootstrap.MonthField,  {
22759     
22760     content : {
22761         tag: 'tbody',
22762         cn: [
22763         {
22764             tag: 'tr',
22765             cn: [
22766             {
22767                 tag: 'td',
22768                 colspan: '7'
22769             }
22770             ]
22771         }
22772         ]
22773     },
22774     
22775     dates:{
22776         en: {
22777             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22778             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22779         }
22780     }
22781 });
22782
22783 Roo.apply(Roo.bootstrap.MonthField,  {
22784   
22785     template : {
22786         tag: 'div',
22787         cls: 'datepicker dropdown-menu roo-dynamic',
22788         cn: [
22789             {
22790                 tag: 'div',
22791                 cls: 'datepicker-months',
22792                 cn: [
22793                 {
22794                     tag: 'table',
22795                     cls: 'table-condensed',
22796                     cn:[
22797                         Roo.bootstrap.DateField.content
22798                     ]
22799                 }
22800                 ]
22801             }
22802         ]
22803     }
22804 });
22805
22806  
22807
22808  
22809  /*
22810  * - LGPL
22811  *
22812  * CheckBox
22813  * 
22814  */
22815
22816 /**
22817  * @class Roo.bootstrap.CheckBox
22818  * @extends Roo.bootstrap.Input
22819  * Bootstrap CheckBox class
22820  * 
22821  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22822  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22823  * @cfg {String} boxLabel The text that appears beside the checkbox
22824  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22825  * @cfg {Boolean} checked initnal the element
22826  * @cfg {Boolean} inline inline the element (default false)
22827  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22828  * @cfg {String} tooltip label tooltip
22829  * 
22830  * @constructor
22831  * Create a new CheckBox
22832  * @param {Object} config The config object
22833  */
22834
22835 Roo.bootstrap.CheckBox = function(config){
22836     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22837    
22838     this.addEvents({
22839         /**
22840         * @event check
22841         * Fires when the element is checked or unchecked.
22842         * @param {Roo.bootstrap.CheckBox} this This input
22843         * @param {Boolean} checked The new checked value
22844         */
22845        check : true,
22846        /**
22847         * @event click
22848         * Fires when the element is click.
22849         * @param {Roo.bootstrap.CheckBox} this This input
22850         */
22851        click : true
22852     });
22853     
22854 };
22855
22856 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22857   
22858     inputType: 'checkbox',
22859     inputValue: 1,
22860     valueOff: 0,
22861     boxLabel: false,
22862     checked: false,
22863     weight : false,
22864     inline: false,
22865     tooltip : '',
22866     
22867     // checkbox success does not make any sense really.. 
22868     invalidClass : "",
22869     validClass : "",
22870     
22871     
22872     getAutoCreate : function()
22873     {
22874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22875         
22876         var id = Roo.id();
22877         
22878         var cfg = {};
22879         
22880         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22881         
22882         if(this.inline){
22883             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22884         }
22885         
22886         var input =  {
22887             tag: 'input',
22888             id : id,
22889             type : this.inputType,
22890             value : this.inputValue,
22891             cls : 'roo-' + this.inputType, //'form-box',
22892             placeholder : this.placeholder || ''
22893             
22894         };
22895         
22896         if(this.inputType != 'radio'){
22897             var hidden =  {
22898                 tag: 'input',
22899                 type : 'hidden',
22900                 cls : 'roo-hidden-value',
22901                 value : this.checked ? this.inputValue : this.valueOff
22902             };
22903         }
22904         
22905             
22906         if (this.weight) { // Validity check?
22907             cfg.cls += " " + this.inputType + "-" + this.weight;
22908         }
22909         
22910         if (this.disabled) {
22911             input.disabled=true;
22912         }
22913         
22914         if(this.checked){
22915             input.checked = this.checked;
22916         }
22917         
22918         if (this.name) {
22919             
22920             input.name = this.name;
22921             
22922             if(this.inputType != 'radio'){
22923                 hidden.name = this.name;
22924                 input.name = '_hidden_' + this.name;
22925             }
22926         }
22927         
22928         if (this.size) {
22929             input.cls += ' input-' + this.size;
22930         }
22931         
22932         var settings=this;
22933         
22934         ['xs','sm','md','lg'].map(function(size){
22935             if (settings[size]) {
22936                 cfg.cls += ' col-' + size + '-' + settings[size];
22937             }
22938         });
22939         
22940         var inputblock = input;
22941          
22942         if (this.before || this.after) {
22943             
22944             inputblock = {
22945                 cls : 'input-group',
22946                 cn :  [] 
22947             };
22948             
22949             if (this.before) {
22950                 inputblock.cn.push({
22951                     tag :'span',
22952                     cls : 'input-group-addon',
22953                     html : this.before
22954                 });
22955             }
22956             
22957             inputblock.cn.push(input);
22958             
22959             if(this.inputType != 'radio'){
22960                 inputblock.cn.push(hidden);
22961             }
22962             
22963             if (this.after) {
22964                 inputblock.cn.push({
22965                     tag :'span',
22966                     cls : 'input-group-addon',
22967                     html : this.after
22968                 });
22969             }
22970             
22971         }
22972         var boxLabelCfg = false;
22973         
22974         if(this.boxLabel){
22975            
22976             boxLabelCfg = {
22977                 tag: 'label',
22978                 //'for': id, // box label is handled by onclick - so no for...
22979                 cls: 'box-label',
22980                 html: this.boxLabel
22981             };
22982             if(this.tooltip){
22983                 boxLabelCfg.tooltip = this.tooltip;
22984             }
22985              
22986         }
22987         
22988         
22989         if (align ==='left' && this.fieldLabel.length) {
22990 //                Roo.log("left and has label");
22991             cfg.cn = [
22992                 {
22993                     tag: 'label',
22994                     'for' :  id,
22995                     cls : 'control-label',
22996                     html : this.fieldLabel
22997                 },
22998                 {
22999                     cls : "", 
23000                     cn: [
23001                         inputblock
23002                     ]
23003                 }
23004             ];
23005             
23006             if (boxLabelCfg) {
23007                 cfg.cn[1].cn.push(boxLabelCfg);
23008             }
23009             
23010             if(this.labelWidth > 12){
23011                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23012             }
23013             
23014             if(this.labelWidth < 13 && this.labelmd == 0){
23015                 this.labelmd = this.labelWidth;
23016             }
23017             
23018             if(this.labellg > 0){
23019                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23020                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23021             }
23022             
23023             if(this.labelmd > 0){
23024                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23025                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23026             }
23027             
23028             if(this.labelsm > 0){
23029                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23030                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23031             }
23032             
23033             if(this.labelxs > 0){
23034                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23035                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23036             }
23037             
23038         } else if ( this.fieldLabel.length) {
23039 //                Roo.log(" label");
23040                 cfg.cn = [
23041                    
23042                     {
23043                         tag: this.boxLabel ? 'span' : 'label',
23044                         'for': id,
23045                         cls: 'control-label box-input-label',
23046                         //cls : 'input-group-addon',
23047                         html : this.fieldLabel
23048                     },
23049                     
23050                     inputblock
23051                     
23052                 ];
23053                 if (boxLabelCfg) {
23054                     cfg.cn.push(boxLabelCfg);
23055                 }
23056
23057         } else {
23058             
23059 //                Roo.log(" no label && no align");
23060                 cfg.cn = [  inputblock ] ;
23061                 if (boxLabelCfg) {
23062                     cfg.cn.push(boxLabelCfg);
23063                 }
23064
23065                 
23066         }
23067         
23068        
23069         
23070         if(this.inputType != 'radio'){
23071             cfg.cn.push(hidden);
23072         }
23073         
23074         return cfg;
23075         
23076     },
23077     
23078     /**
23079      * return the real input element.
23080      */
23081     inputEl: function ()
23082     {
23083         return this.el.select('input.roo-' + this.inputType,true).first();
23084     },
23085     hiddenEl: function ()
23086     {
23087         return this.el.select('input.roo-hidden-value',true).first();
23088     },
23089     
23090     labelEl: function()
23091     {
23092         return this.el.select('label.control-label',true).first();
23093     },
23094     /* depricated... */
23095     
23096     label: function()
23097     {
23098         return this.labelEl();
23099     },
23100     
23101     boxLabelEl: function()
23102     {
23103         return this.el.select('label.box-label',true).first();
23104     },
23105     
23106     initEvents : function()
23107     {
23108 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23109         
23110         this.inputEl().on('click', this.onClick,  this);
23111         
23112         if (this.boxLabel) { 
23113             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23114         }
23115         
23116         this.startValue = this.getValue();
23117         
23118         if(this.groupId){
23119             Roo.bootstrap.CheckBox.register(this);
23120         }
23121     },
23122     
23123     onClick : function(e)
23124     {   
23125         if(this.fireEvent('click', this, e) !== false){
23126             this.setChecked(!this.checked);
23127         }
23128         
23129     },
23130     
23131     setChecked : function(state,suppressEvent)
23132     {
23133         this.startValue = this.getValue();
23134
23135         if(this.inputType == 'radio'){
23136             
23137             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23138                 e.dom.checked = false;
23139             });
23140             
23141             this.inputEl().dom.checked = true;
23142             
23143             this.inputEl().dom.value = this.inputValue;
23144             
23145             if(suppressEvent !== true){
23146                 this.fireEvent('check', this, true);
23147             }
23148             
23149             this.validate();
23150             
23151             return;
23152         }
23153         
23154         this.checked = state;
23155         
23156         this.inputEl().dom.checked = state;
23157         
23158         
23159         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23160         
23161         if(suppressEvent !== true){
23162             this.fireEvent('check', this, state);
23163         }
23164         
23165         this.validate();
23166     },
23167     
23168     getValue : function()
23169     {
23170         if(this.inputType == 'radio'){
23171             return this.getGroupValue();
23172         }
23173         
23174         return this.hiddenEl().dom.value;
23175         
23176     },
23177     
23178     getGroupValue : function()
23179     {
23180         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23181             return '';
23182         }
23183         
23184         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23185     },
23186     
23187     setValue : function(v,suppressEvent)
23188     {
23189         if(this.inputType == 'radio'){
23190             this.setGroupValue(v, suppressEvent);
23191             return;
23192         }
23193         
23194         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23195         
23196         this.validate();
23197     },
23198     
23199     setGroupValue : function(v, suppressEvent)
23200     {
23201         this.startValue = this.getValue();
23202         
23203         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23204             e.dom.checked = false;
23205             
23206             if(e.dom.value == v){
23207                 e.dom.checked = true;
23208             }
23209         });
23210         
23211         if(suppressEvent !== true){
23212             this.fireEvent('check', this, true);
23213         }
23214
23215         this.validate();
23216         
23217         return;
23218     },
23219     
23220     validate : function()
23221     {
23222         if(this.getVisibilityEl().hasClass('hidden')){
23223             return true;
23224         }
23225         
23226         if(
23227                 this.disabled || 
23228                 (this.inputType == 'radio' && this.validateRadio()) ||
23229                 (this.inputType == 'checkbox' && this.validateCheckbox())
23230         ){
23231             this.markValid();
23232             return true;
23233         }
23234         
23235         this.markInvalid();
23236         return false;
23237     },
23238     
23239     validateRadio : function()
23240     {
23241         if(this.getVisibilityEl().hasClass('hidden')){
23242             return true;
23243         }
23244         
23245         if(this.allowBlank){
23246             return true;
23247         }
23248         
23249         var valid = false;
23250         
23251         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23252             if(!e.dom.checked){
23253                 return;
23254             }
23255             
23256             valid = true;
23257             
23258             return false;
23259         });
23260         
23261         return valid;
23262     },
23263     
23264     validateCheckbox : function()
23265     {
23266         if(!this.groupId){
23267             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23268             //return (this.getValue() == this.inputValue) ? true : false;
23269         }
23270         
23271         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23272         
23273         if(!group){
23274             return false;
23275         }
23276         
23277         var r = false;
23278         
23279         for(var i in group){
23280             if(group[i].el.isVisible(true)){
23281                 r = false;
23282                 break;
23283             }
23284             
23285             r = true;
23286         }
23287         
23288         for(var i in group){
23289             if(r){
23290                 break;
23291             }
23292             
23293             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23294         }
23295         
23296         return r;
23297     },
23298     
23299     /**
23300      * Mark this field as valid
23301      */
23302     markValid : function()
23303     {
23304         var _this = this;
23305         
23306         this.fireEvent('valid', this);
23307         
23308         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23309         
23310         if(this.groupId){
23311             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23312         }
23313         
23314         if(label){
23315             label.markValid();
23316         }
23317
23318         if(this.inputType == 'radio'){
23319             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23320                 var fg = e.findParent('.form-group', false, true);
23321                 if (Roo.bootstrap.version == 3) {
23322                     fg.removeClass([_this.invalidClass, _this.validClass]);
23323                     fg.addClass(_this.validClass);
23324                 } else {
23325                     fg.removeClass(['is-valid', 'is-invalid']);
23326                     fg.addClass('is-valid');
23327                 }
23328             });
23329             
23330             return;
23331         }
23332
23333         if(!this.groupId){
23334             var fg = this.el.findParent('.form-group', false, true);
23335             if (Roo.bootstrap.version == 3) {
23336                 fg.removeClass([this.invalidClass, this.validClass]);
23337                 fg.addClass(this.validClass);
23338             } else {
23339                 fg.removeClass(['is-valid', 'is-invalid']);
23340                 fg.addClass('is-valid');
23341             }
23342             return;
23343         }
23344         
23345         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23346         
23347         if(!group){
23348             return;
23349         }
23350         
23351         for(var i in group){
23352             var fg = group[i].el.findParent('.form-group', false, true);
23353             if (Roo.bootstrap.version == 3) {
23354                 fg.removeClass([this.invalidClass, this.validClass]);
23355                 fg.addClass(this.validClass);
23356             } else {
23357                 fg.removeClass(['is-valid', 'is-invalid']);
23358                 fg.addClass('is-valid');
23359             }
23360         }
23361     },
23362     
23363      /**
23364      * Mark this field as invalid
23365      * @param {String} msg The validation message
23366      */
23367     markInvalid : function(msg)
23368     {
23369         if(this.allowBlank){
23370             return;
23371         }
23372         
23373         var _this = this;
23374         
23375         this.fireEvent('invalid', this, msg);
23376         
23377         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23378         
23379         if(this.groupId){
23380             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23381         }
23382         
23383         if(label){
23384             label.markInvalid();
23385         }
23386             
23387         if(this.inputType == 'radio'){
23388             
23389             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23390                 var fg = e.findParent('.form-group', false, true);
23391                 if (Roo.bootstrap.version == 3) {
23392                     fg.removeClass([_this.invalidClass, _this.validClass]);
23393                     fg.addClass(_this.invalidClass);
23394                 } else {
23395                     fg.removeClass(['is-invalid', 'is-valid']);
23396                     fg.addClass('is-invalid');
23397                 }
23398             });
23399             
23400             return;
23401         }
23402         
23403         if(!this.groupId){
23404             var fg = this.el.findParent('.form-group', false, true);
23405             if (Roo.bootstrap.version == 3) {
23406                 fg.removeClass([_this.invalidClass, _this.validClass]);
23407                 fg.addClass(_this.invalidClass);
23408             } else {
23409                 fg.removeClass(['is-invalid', 'is-valid']);
23410                 fg.addClass('is-invalid');
23411             }
23412             return;
23413         }
23414         
23415         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23416         
23417         if(!group){
23418             return;
23419         }
23420         
23421         for(var i in group){
23422             var fg = group[i].el.findParent('.form-group', false, true);
23423             if (Roo.bootstrap.version == 3) {
23424                 fg.removeClass([_this.invalidClass, _this.validClass]);
23425                 fg.addClass(_this.invalidClass);
23426             } else {
23427                 fg.removeClass(['is-invalid', 'is-valid']);
23428                 fg.addClass('is-invalid');
23429             }
23430         }
23431         
23432     },
23433     
23434     clearInvalid : function()
23435     {
23436         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23437         
23438         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23439         
23440         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23441         
23442         if (label && label.iconEl) {
23443             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23444             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23445         }
23446     },
23447     
23448     disable : function()
23449     {
23450         if(this.inputType != 'radio'){
23451             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23452             return;
23453         }
23454         
23455         var _this = this;
23456         
23457         if(this.rendered){
23458             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23459                 _this.getActionEl().addClass(this.disabledClass);
23460                 e.dom.disabled = true;
23461             });
23462         }
23463         
23464         this.disabled = true;
23465         this.fireEvent("disable", this);
23466         return this;
23467     },
23468
23469     enable : function()
23470     {
23471         if(this.inputType != 'radio'){
23472             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23473             return;
23474         }
23475         
23476         var _this = this;
23477         
23478         if(this.rendered){
23479             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23480                 _this.getActionEl().removeClass(this.disabledClass);
23481                 e.dom.disabled = false;
23482             });
23483         }
23484         
23485         this.disabled = false;
23486         this.fireEvent("enable", this);
23487         return this;
23488     },
23489     
23490     setBoxLabel : function(v)
23491     {
23492         this.boxLabel = v;
23493         
23494         if(this.rendered){
23495             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23496         }
23497     }
23498
23499 });
23500
23501 Roo.apply(Roo.bootstrap.CheckBox, {
23502     
23503     groups: {},
23504     
23505      /**
23506     * register a CheckBox Group
23507     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23508     */
23509     register : function(checkbox)
23510     {
23511         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23512             this.groups[checkbox.groupId] = {};
23513         }
23514         
23515         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23516             return;
23517         }
23518         
23519         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23520         
23521     },
23522     /**
23523     * fetch a CheckBox Group based on the group ID
23524     * @param {string} the group ID
23525     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23526     */
23527     get: function(groupId) {
23528         if (typeof(this.groups[groupId]) == 'undefined') {
23529             return false;
23530         }
23531         
23532         return this.groups[groupId] ;
23533     }
23534     
23535     
23536 });
23537 /*
23538  * - LGPL
23539  *
23540  * RadioItem
23541  * 
23542  */
23543
23544 /**
23545  * @class Roo.bootstrap.Radio
23546  * @extends Roo.bootstrap.Component
23547  * Bootstrap Radio class
23548  * @cfg {String} boxLabel - the label associated
23549  * @cfg {String} value - the value of radio
23550  * 
23551  * @constructor
23552  * Create a new Radio
23553  * @param {Object} config The config object
23554  */
23555 Roo.bootstrap.Radio = function(config){
23556     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23557     
23558 };
23559
23560 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23561     
23562     boxLabel : '',
23563     
23564     value : '',
23565     
23566     getAutoCreate : function()
23567     {
23568         var cfg = {
23569             tag : 'div',
23570             cls : 'form-group radio',
23571             cn : [
23572                 {
23573                     tag : 'label',
23574                     cls : 'box-label',
23575                     html : this.boxLabel
23576                 }
23577             ]
23578         };
23579         
23580         return cfg;
23581     },
23582     
23583     initEvents : function() 
23584     {
23585         this.parent().register(this);
23586         
23587         this.el.on('click', this.onClick, this);
23588         
23589     },
23590     
23591     onClick : function(e)
23592     {
23593         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23594             this.setChecked(true);
23595         }
23596     },
23597     
23598     setChecked : function(state, suppressEvent)
23599     {
23600         this.parent().setValue(this.value, suppressEvent);
23601         
23602     },
23603     
23604     setBoxLabel : function(v)
23605     {
23606         this.boxLabel = v;
23607         
23608         if(this.rendered){
23609             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23610         }
23611     }
23612     
23613 });
23614  
23615
23616  /*
23617  * - LGPL
23618  *
23619  * Input
23620  * 
23621  */
23622
23623 /**
23624  * @class Roo.bootstrap.SecurePass
23625  * @extends Roo.bootstrap.Input
23626  * Bootstrap SecurePass class
23627  *
23628  * 
23629  * @constructor
23630  * Create a new SecurePass
23631  * @param {Object} config The config object
23632  */
23633  
23634 Roo.bootstrap.SecurePass = function (config) {
23635     // these go here, so the translation tool can replace them..
23636     this.errors = {
23637         PwdEmpty: "Please type a password, and then retype it to confirm.",
23638         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23639         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23640         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23641         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23642         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23643         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23644         TooWeak: "Your password is Too Weak."
23645     },
23646     this.meterLabel = "Password strength:";
23647     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23648     this.meterClass = [
23649         "roo-password-meter-tooweak", 
23650         "roo-password-meter-weak", 
23651         "roo-password-meter-medium", 
23652         "roo-password-meter-strong", 
23653         "roo-password-meter-grey"
23654     ];
23655     
23656     this.errors = {};
23657     
23658     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23659 }
23660
23661 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23662     /**
23663      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23664      * {
23665      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23666      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23667      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23668      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23669      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23670      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23671      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23672      * })
23673      */
23674     // private
23675     
23676     meterWidth: 300,
23677     errorMsg :'',    
23678     errors: false,
23679     imageRoot: '/',
23680     /**
23681      * @cfg {String/Object} Label for the strength meter (defaults to
23682      * 'Password strength:')
23683      */
23684     // private
23685     meterLabel: '',
23686     /**
23687      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23688      * ['Weak', 'Medium', 'Strong'])
23689      */
23690     // private    
23691     pwdStrengths: false,    
23692     // private
23693     strength: 0,
23694     // private
23695     _lastPwd: null,
23696     // private
23697     kCapitalLetter: 0,
23698     kSmallLetter: 1,
23699     kDigit: 2,
23700     kPunctuation: 3,
23701     
23702     insecure: false,
23703     // private
23704     initEvents: function ()
23705     {
23706         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23707
23708         if (this.el.is('input[type=password]') && Roo.isSafari) {
23709             this.el.on('keydown', this.SafariOnKeyDown, this);
23710         }
23711
23712         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23713     },
23714     // private
23715     onRender: function (ct, position)
23716     {
23717         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23718         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23719         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23720
23721         this.trigger.createChild({
23722                    cn: [
23723                     {
23724                     //id: 'PwdMeter',
23725                     tag: 'div',
23726                     cls: 'roo-password-meter-grey col-xs-12',
23727                     style: {
23728                         //width: 0,
23729                         //width: this.meterWidth + 'px'                                                
23730                         }
23731                     },
23732                     {                            
23733                          cls: 'roo-password-meter-text'                          
23734                     }
23735                 ]            
23736         });
23737
23738          
23739         if (this.hideTrigger) {
23740             this.trigger.setDisplayed(false);
23741         }
23742         this.setSize(this.width || '', this.height || '');
23743     },
23744     // private
23745     onDestroy: function ()
23746     {
23747         if (this.trigger) {
23748             this.trigger.removeAllListeners();
23749             this.trigger.remove();
23750         }
23751         if (this.wrap) {
23752             this.wrap.remove();
23753         }
23754         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23755     },
23756     // private
23757     checkStrength: function ()
23758     {
23759         var pwd = this.inputEl().getValue();
23760         if (pwd == this._lastPwd) {
23761             return;
23762         }
23763
23764         var strength;
23765         if (this.ClientSideStrongPassword(pwd)) {
23766             strength = 3;
23767         } else if (this.ClientSideMediumPassword(pwd)) {
23768             strength = 2;
23769         } else if (this.ClientSideWeakPassword(pwd)) {
23770             strength = 1;
23771         } else {
23772             strength = 0;
23773         }
23774         
23775         Roo.log('strength1: ' + strength);
23776         
23777         //var pm = this.trigger.child('div/div/div').dom;
23778         var pm = this.trigger.child('div/div');
23779         pm.removeClass(this.meterClass);
23780         pm.addClass(this.meterClass[strength]);
23781                 
23782         
23783         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23784                 
23785         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23786         
23787         this._lastPwd = pwd;
23788     },
23789     reset: function ()
23790     {
23791         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23792         
23793         this._lastPwd = '';
23794         
23795         var pm = this.trigger.child('div/div');
23796         pm.removeClass(this.meterClass);
23797         pm.addClass('roo-password-meter-grey');        
23798         
23799         
23800         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23801         
23802         pt.innerHTML = '';
23803         this.inputEl().dom.type='password';
23804     },
23805     // private
23806     validateValue: function (value)
23807     {
23808         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23809             return false;
23810         }
23811         if (value.length == 0) {
23812             if (this.allowBlank) {
23813                 this.clearInvalid();
23814                 return true;
23815             }
23816
23817             this.markInvalid(this.errors.PwdEmpty);
23818             this.errorMsg = this.errors.PwdEmpty;
23819             return false;
23820         }
23821         
23822         if(this.insecure){
23823             return true;
23824         }
23825         
23826         if (!value.match(/[\x21-\x7e]+/)) {
23827             this.markInvalid(this.errors.PwdBadChar);
23828             this.errorMsg = this.errors.PwdBadChar;
23829             return false;
23830         }
23831         if (value.length < 6) {
23832             this.markInvalid(this.errors.PwdShort);
23833             this.errorMsg = this.errors.PwdShort;
23834             return false;
23835         }
23836         if (value.length > 16) {
23837             this.markInvalid(this.errors.PwdLong);
23838             this.errorMsg = this.errors.PwdLong;
23839             return false;
23840         }
23841         var strength;
23842         if (this.ClientSideStrongPassword(value)) {
23843             strength = 3;
23844         } else if (this.ClientSideMediumPassword(value)) {
23845             strength = 2;
23846         } else if (this.ClientSideWeakPassword(value)) {
23847             strength = 1;
23848         } else {
23849             strength = 0;
23850         }
23851
23852         
23853         if (strength < 2) {
23854             //this.markInvalid(this.errors.TooWeak);
23855             this.errorMsg = this.errors.TooWeak;
23856             //return false;
23857         }
23858         
23859         
23860         console.log('strength2: ' + strength);
23861         
23862         //var pm = this.trigger.child('div/div/div').dom;
23863         
23864         var pm = this.trigger.child('div/div');
23865         pm.removeClass(this.meterClass);
23866         pm.addClass(this.meterClass[strength]);
23867                 
23868         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23869                 
23870         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23871         
23872         this.errorMsg = ''; 
23873         return true;
23874     },
23875     // private
23876     CharacterSetChecks: function (type)
23877     {
23878         this.type = type;
23879         this.fResult = false;
23880     },
23881     // private
23882     isctype: function (character, type)
23883     {
23884         switch (type) {  
23885             case this.kCapitalLetter:
23886                 if (character >= 'A' && character <= 'Z') {
23887                     return true;
23888                 }
23889                 break;
23890             
23891             case this.kSmallLetter:
23892                 if (character >= 'a' && character <= 'z') {
23893                     return true;
23894                 }
23895                 break;
23896             
23897             case this.kDigit:
23898                 if (character >= '0' && character <= '9') {
23899                     return true;
23900                 }
23901                 break;
23902             
23903             case this.kPunctuation:
23904                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23905                     return true;
23906                 }
23907                 break;
23908             
23909             default:
23910                 return false;
23911         }
23912
23913     },
23914     // private
23915     IsLongEnough: function (pwd, size)
23916     {
23917         return !(pwd == null || isNaN(size) || pwd.length < size);
23918     },
23919     // private
23920     SpansEnoughCharacterSets: function (word, nb)
23921     {
23922         if (!this.IsLongEnough(word, nb))
23923         {
23924             return false;
23925         }
23926
23927         var characterSetChecks = new Array(
23928             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23929             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23930         );
23931         
23932         for (var index = 0; index < word.length; ++index) {
23933             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23934                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23935                     characterSetChecks[nCharSet].fResult = true;
23936                     break;
23937                 }
23938             }
23939         }
23940
23941         var nCharSets = 0;
23942         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23943             if (characterSetChecks[nCharSet].fResult) {
23944                 ++nCharSets;
23945             }
23946         }
23947
23948         if (nCharSets < nb) {
23949             return false;
23950         }
23951         return true;
23952     },
23953     // private
23954     ClientSideStrongPassword: function (pwd)
23955     {
23956         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23957     },
23958     // private
23959     ClientSideMediumPassword: function (pwd)
23960     {
23961         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23962     },
23963     // private
23964     ClientSideWeakPassword: function (pwd)
23965     {
23966         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23967     }
23968           
23969 })//<script type="text/javascript">
23970
23971 /*
23972  * Based  Ext JS Library 1.1.1
23973  * Copyright(c) 2006-2007, Ext JS, LLC.
23974  * LGPL
23975  *
23976  */
23977  
23978 /**
23979  * @class Roo.HtmlEditorCore
23980  * @extends Roo.Component
23981  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23982  *
23983  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23984  */
23985
23986 Roo.HtmlEditorCore = function(config){
23987     
23988     
23989     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23990     
23991     
23992     this.addEvents({
23993         /**
23994          * @event initialize
23995          * Fires when the editor is fully initialized (including the iframe)
23996          * @param {Roo.HtmlEditorCore} this
23997          */
23998         initialize: true,
23999         /**
24000          * @event activate
24001          * Fires when the editor is first receives the focus. Any insertion must wait
24002          * until after this event.
24003          * @param {Roo.HtmlEditorCore} this
24004          */
24005         activate: true,
24006          /**
24007          * @event beforesync
24008          * Fires before the textarea is updated with content from the editor iframe. Return false
24009          * to cancel the sync.
24010          * @param {Roo.HtmlEditorCore} this
24011          * @param {String} html
24012          */
24013         beforesync: true,
24014          /**
24015          * @event beforepush
24016          * Fires before the iframe editor is updated with content from the textarea. Return false
24017          * to cancel the push.
24018          * @param {Roo.HtmlEditorCore} this
24019          * @param {String} html
24020          */
24021         beforepush: true,
24022          /**
24023          * @event sync
24024          * Fires when the textarea is updated with content from the editor iframe.
24025          * @param {Roo.HtmlEditorCore} this
24026          * @param {String} html
24027          */
24028         sync: true,
24029          /**
24030          * @event push
24031          * Fires when the iframe editor is updated with content from the textarea.
24032          * @param {Roo.HtmlEditorCore} this
24033          * @param {String} html
24034          */
24035         push: true,
24036         
24037         /**
24038          * @event editorevent
24039          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24040          * @param {Roo.HtmlEditorCore} this
24041          */
24042         editorevent: true
24043         
24044     });
24045     
24046     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24047     
24048     // defaults : white / black...
24049     this.applyBlacklists();
24050     
24051     
24052     
24053 };
24054
24055
24056 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24057
24058
24059      /**
24060      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24061      */
24062     
24063     owner : false,
24064     
24065      /**
24066      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24067      *                        Roo.resizable.
24068      */
24069     resizable : false,
24070      /**
24071      * @cfg {Number} height (in pixels)
24072      */   
24073     height: 300,
24074    /**
24075      * @cfg {Number} width (in pixels)
24076      */   
24077     width: 500,
24078     
24079     /**
24080      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24081      * 
24082      */
24083     stylesheets: false,
24084     
24085     // id of frame..
24086     frameId: false,
24087     
24088     // private properties
24089     validationEvent : false,
24090     deferHeight: true,
24091     initialized : false,
24092     activated : false,
24093     sourceEditMode : false,
24094     onFocus : Roo.emptyFn,
24095     iframePad:3,
24096     hideMode:'offsets',
24097     
24098     clearUp: true,
24099     
24100     // blacklist + whitelisted elements..
24101     black: false,
24102     white: false,
24103      
24104     bodyCls : '',
24105
24106     /**
24107      * Protected method that will not generally be called directly. It
24108      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24109      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24110      */
24111     getDocMarkup : function(){
24112         // body styles..
24113         var st = '';
24114         
24115         // inherit styels from page...?? 
24116         if (this.stylesheets === false) {
24117             
24118             Roo.get(document.head).select('style').each(function(node) {
24119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24120             });
24121             
24122             Roo.get(document.head).select('link').each(function(node) { 
24123                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24124             });
24125             
24126         } else if (!this.stylesheets.length) {
24127                 // simple..
24128                 st = '<style type="text/css">' +
24129                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24130                    '</style>';
24131         } else {
24132             for (var i in this.stylesheets) { 
24133                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24134             }
24135             
24136         }
24137         
24138         st +=  '<style type="text/css">' +
24139             'IMG { cursor: pointer } ' +
24140         '</style>';
24141
24142         var cls = 'roo-htmleditor-body';
24143         
24144         if(this.bodyCls.length){
24145             cls += ' ' + this.bodyCls;
24146         }
24147         
24148         return '<html><head>' + st  +
24149             //<style type="text/css">' +
24150             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24151             //'</style>' +
24152             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24153     },
24154
24155     // private
24156     onRender : function(ct, position)
24157     {
24158         var _t = this;
24159         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24160         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24161         
24162         
24163         this.el.dom.style.border = '0 none';
24164         this.el.dom.setAttribute('tabIndex', -1);
24165         this.el.addClass('x-hidden hide');
24166         
24167         
24168         
24169         if(Roo.isIE){ // fix IE 1px bogus margin
24170             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24171         }
24172        
24173         
24174         this.frameId = Roo.id();
24175         
24176          
24177         
24178         var iframe = this.owner.wrap.createChild({
24179             tag: 'iframe',
24180             cls: 'form-control', // bootstrap..
24181             id: this.frameId,
24182             name: this.frameId,
24183             frameBorder : 'no',
24184             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24185         }, this.el
24186         );
24187         
24188         
24189         this.iframe = iframe.dom;
24190
24191          this.assignDocWin();
24192         
24193         this.doc.designMode = 'on';
24194        
24195         this.doc.open();
24196         this.doc.write(this.getDocMarkup());
24197         this.doc.close();
24198
24199         
24200         var task = { // must defer to wait for browser to be ready
24201             run : function(){
24202                 //console.log("run task?" + this.doc.readyState);
24203                 this.assignDocWin();
24204                 if(this.doc.body || this.doc.readyState == 'complete'){
24205                     try {
24206                         this.doc.designMode="on";
24207                     } catch (e) {
24208                         return;
24209                     }
24210                     Roo.TaskMgr.stop(task);
24211                     this.initEditor.defer(10, this);
24212                 }
24213             },
24214             interval : 10,
24215             duration: 10000,
24216             scope: this
24217         };
24218         Roo.TaskMgr.start(task);
24219
24220     },
24221
24222     // private
24223     onResize : function(w, h)
24224     {
24225          Roo.log('resize: ' +w + ',' + h );
24226         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24227         if(!this.iframe){
24228             return;
24229         }
24230         if(typeof w == 'number'){
24231             
24232             this.iframe.style.width = w + 'px';
24233         }
24234         if(typeof h == 'number'){
24235             
24236             this.iframe.style.height = h + 'px';
24237             if(this.doc){
24238                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24239             }
24240         }
24241         
24242     },
24243
24244     /**
24245      * Toggles the editor between standard and source edit mode.
24246      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24247      */
24248     toggleSourceEdit : function(sourceEditMode){
24249         
24250         this.sourceEditMode = sourceEditMode === true;
24251         
24252         if(this.sourceEditMode){
24253  
24254             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24255             
24256         }else{
24257             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24258             //this.iframe.className = '';
24259             this.deferFocus();
24260         }
24261         //this.setSize(this.owner.wrap.getSize());
24262         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24263     },
24264
24265     
24266   
24267
24268     /**
24269      * Protected method that will not generally be called directly. If you need/want
24270      * custom HTML cleanup, this is the method you should override.
24271      * @param {String} html The HTML to be cleaned
24272      * return {String} The cleaned HTML
24273      */
24274     cleanHtml : function(html){
24275         html = String(html);
24276         if(html.length > 5){
24277             if(Roo.isSafari){ // strip safari nonsense
24278                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24279             }
24280         }
24281         if(html == '&nbsp;'){
24282             html = '';
24283         }
24284         return html;
24285     },
24286
24287     /**
24288      * HTML Editor -> Textarea
24289      * Protected method that will not generally be called directly. Syncs the contents
24290      * of the editor iframe with the textarea.
24291      */
24292     syncValue : function(){
24293         if(this.initialized){
24294             var bd = (this.doc.body || this.doc.documentElement);
24295             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24296             var html = bd.innerHTML;
24297             if(Roo.isSafari){
24298                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24299                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24300                 if(m && m[1]){
24301                     html = '<div style="'+m[0]+'">' + html + '</div>';
24302                 }
24303             }
24304             html = this.cleanHtml(html);
24305             // fix up the special chars.. normaly like back quotes in word...
24306             // however we do not want to do this with chinese..
24307             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24308                 
24309                 var cc = match.charCodeAt();
24310
24311                 // Get the character value, handling surrogate pairs
24312                 if (match.length == 2) {
24313                     // It's a surrogate pair, calculate the Unicode code point
24314                     var high = match.charCodeAt(0) - 0xD800;
24315                     var low  = match.charCodeAt(1) - 0xDC00;
24316                     cc = (high * 0x400) + low + 0x10000;
24317                 }  else if (
24318                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24319                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24320                     (cc >= 0xf900 && cc < 0xfb00 )
24321                 ) {
24322                         return match;
24323                 }  
24324          
24325                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24326                 return "&#" + cc + ";";
24327                 
24328                 
24329             });
24330             
24331             
24332              
24333             if(this.owner.fireEvent('beforesync', this, html) !== false){
24334                 this.el.dom.value = html;
24335                 this.owner.fireEvent('sync', this, html);
24336             }
24337         }
24338     },
24339
24340     /**
24341      * Protected method that will not generally be called directly. Pushes the value of the textarea
24342      * into the iframe editor.
24343      */
24344     pushValue : function(){
24345         if(this.initialized){
24346             var v = this.el.dom.value.trim();
24347             
24348 //            if(v.length < 1){
24349 //                v = '&#160;';
24350 //            }
24351             
24352             if(this.owner.fireEvent('beforepush', this, v) !== false){
24353                 var d = (this.doc.body || this.doc.documentElement);
24354                 d.innerHTML = v;
24355                 this.cleanUpPaste();
24356                 this.el.dom.value = d.innerHTML;
24357                 this.owner.fireEvent('push', this, v);
24358             }
24359         }
24360     },
24361
24362     // private
24363     deferFocus : function(){
24364         this.focus.defer(10, this);
24365     },
24366
24367     // doc'ed in Field
24368     focus : function(){
24369         if(this.win && !this.sourceEditMode){
24370             this.win.focus();
24371         }else{
24372             this.el.focus();
24373         }
24374     },
24375     
24376     assignDocWin: function()
24377     {
24378         var iframe = this.iframe;
24379         
24380          if(Roo.isIE){
24381             this.doc = iframe.contentWindow.document;
24382             this.win = iframe.contentWindow;
24383         } else {
24384 //            if (!Roo.get(this.frameId)) {
24385 //                return;
24386 //            }
24387 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24388 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24389             
24390             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24391                 return;
24392             }
24393             
24394             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24395             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24396         }
24397     },
24398     
24399     // private
24400     initEditor : function(){
24401         //console.log("INIT EDITOR");
24402         this.assignDocWin();
24403         
24404         
24405         
24406         this.doc.designMode="on";
24407         this.doc.open();
24408         this.doc.write(this.getDocMarkup());
24409         this.doc.close();
24410         
24411         var dbody = (this.doc.body || this.doc.documentElement);
24412         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24413         // this copies styles from the containing element into thsi one..
24414         // not sure why we need all of this..
24415         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24416         
24417         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24418         //ss['background-attachment'] = 'fixed'; // w3c
24419         dbody.bgProperties = 'fixed'; // ie
24420         //Roo.DomHelper.applyStyles(dbody, ss);
24421         Roo.EventManager.on(this.doc, {
24422             //'mousedown': this.onEditorEvent,
24423             'mouseup': this.onEditorEvent,
24424             'dblclick': this.onEditorEvent,
24425             'click': this.onEditorEvent,
24426             'keyup': this.onEditorEvent,
24427             buffer:100,
24428             scope: this
24429         });
24430         if(Roo.isGecko){
24431             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24432         }
24433         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24434             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24435         }
24436         this.initialized = true;
24437
24438         this.owner.fireEvent('initialize', this);
24439         this.pushValue();
24440     },
24441
24442     // private
24443     onDestroy : function(){
24444         
24445         
24446         
24447         if(this.rendered){
24448             
24449             //for (var i =0; i < this.toolbars.length;i++) {
24450             //    // fixme - ask toolbars for heights?
24451             //    this.toolbars[i].onDestroy();
24452            // }
24453             
24454             //this.wrap.dom.innerHTML = '';
24455             //this.wrap.remove();
24456         }
24457     },
24458
24459     // private
24460     onFirstFocus : function(){
24461         
24462         this.assignDocWin();
24463         
24464         
24465         this.activated = true;
24466          
24467     
24468         if(Roo.isGecko){ // prevent silly gecko errors
24469             this.win.focus();
24470             var s = this.win.getSelection();
24471             if(!s.focusNode || s.focusNode.nodeType != 3){
24472                 var r = s.getRangeAt(0);
24473                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24474                 r.collapse(true);
24475                 this.deferFocus();
24476             }
24477             try{
24478                 this.execCmd('useCSS', true);
24479                 this.execCmd('styleWithCSS', false);
24480             }catch(e){}
24481         }
24482         this.owner.fireEvent('activate', this);
24483     },
24484
24485     // private
24486     adjustFont: function(btn){
24487         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24488         //if(Roo.isSafari){ // safari
24489         //    adjust *= 2;
24490        // }
24491         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24492         if(Roo.isSafari){ // safari
24493             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24494             v =  (v < 10) ? 10 : v;
24495             v =  (v > 48) ? 48 : v;
24496             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24497             
24498         }
24499         
24500         
24501         v = Math.max(1, v+adjust);
24502         
24503         this.execCmd('FontSize', v  );
24504     },
24505
24506     onEditorEvent : function(e)
24507     {
24508         this.owner.fireEvent('editorevent', this, e);
24509       //  this.updateToolbar();
24510         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24511     },
24512
24513     insertTag : function(tg)
24514     {
24515         // could be a bit smarter... -> wrap the current selected tRoo..
24516         if (tg.toLowerCase() == 'span' ||
24517             tg.toLowerCase() == 'code' ||
24518             tg.toLowerCase() == 'sup' ||
24519             tg.toLowerCase() == 'sub' 
24520             ) {
24521             
24522             range = this.createRange(this.getSelection());
24523             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24524             wrappingNode.appendChild(range.extractContents());
24525             range.insertNode(wrappingNode);
24526
24527             return;
24528             
24529             
24530             
24531         }
24532         this.execCmd("formatblock",   tg);
24533         
24534     },
24535     
24536     insertText : function(txt)
24537     {
24538         
24539         
24540         var range = this.createRange();
24541         range.deleteContents();
24542                //alert(Sender.getAttribute('label'));
24543                
24544         range.insertNode(this.doc.createTextNode(txt));
24545     } ,
24546     
24547      
24548
24549     /**
24550      * Executes a Midas editor command on the editor document and performs necessary focus and
24551      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24552      * @param {String} cmd The Midas command
24553      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24554      */
24555     relayCmd : function(cmd, value){
24556         this.win.focus();
24557         this.execCmd(cmd, value);
24558         this.owner.fireEvent('editorevent', this);
24559         //this.updateToolbar();
24560         this.owner.deferFocus();
24561     },
24562
24563     /**
24564      * Executes a Midas editor command directly on the editor document.
24565      * For visual commands, you should use {@link #relayCmd} instead.
24566      * <b>This should only be called after the editor is initialized.</b>
24567      * @param {String} cmd The Midas command
24568      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24569      */
24570     execCmd : function(cmd, value){
24571         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24572         this.syncValue();
24573     },
24574  
24575  
24576    
24577     /**
24578      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24579      * to insert tRoo.
24580      * @param {String} text | dom node.. 
24581      */
24582     insertAtCursor : function(text)
24583     {
24584         
24585         if(!this.activated){
24586             return;
24587         }
24588         /*
24589         if(Roo.isIE){
24590             this.win.focus();
24591             var r = this.doc.selection.createRange();
24592             if(r){
24593                 r.collapse(true);
24594                 r.pasteHTML(text);
24595                 this.syncValue();
24596                 this.deferFocus();
24597             
24598             }
24599             return;
24600         }
24601         */
24602         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24603             this.win.focus();
24604             
24605             
24606             // from jquery ui (MIT licenced)
24607             var range, node;
24608             var win = this.win;
24609             
24610             if (win.getSelection && win.getSelection().getRangeAt) {
24611                 range = win.getSelection().getRangeAt(0);
24612                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24613                 range.insertNode(node);
24614             } else if (win.document.selection && win.document.selection.createRange) {
24615                 // no firefox support
24616                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24617                 win.document.selection.createRange().pasteHTML(txt);
24618             } else {
24619                 // no firefox support
24620                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24621                 this.execCmd('InsertHTML', txt);
24622             } 
24623             
24624             this.syncValue();
24625             
24626             this.deferFocus();
24627         }
24628     },
24629  // private
24630     mozKeyPress : function(e){
24631         if(e.ctrlKey){
24632             var c = e.getCharCode(), cmd;
24633           
24634             if(c > 0){
24635                 c = String.fromCharCode(c).toLowerCase();
24636                 switch(c){
24637                     case 'b':
24638                         cmd = 'bold';
24639                         break;
24640                     case 'i':
24641                         cmd = 'italic';
24642                         break;
24643                     
24644                     case 'u':
24645                         cmd = 'underline';
24646                         break;
24647                     
24648                     case 'v':
24649                         this.cleanUpPaste.defer(100, this);
24650                         return;
24651                         
24652                 }
24653                 if(cmd){
24654                     this.win.focus();
24655                     this.execCmd(cmd);
24656                     this.deferFocus();
24657                     e.preventDefault();
24658                 }
24659                 
24660             }
24661         }
24662     },
24663
24664     // private
24665     fixKeys : function(){ // load time branching for fastest keydown performance
24666         if(Roo.isIE){
24667             return function(e){
24668                 var k = e.getKey(), r;
24669                 if(k == e.TAB){
24670                     e.stopEvent();
24671                     r = this.doc.selection.createRange();
24672                     if(r){
24673                         r.collapse(true);
24674                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24675                         this.deferFocus();
24676                     }
24677                     return;
24678                 }
24679                 
24680                 if(k == e.ENTER){
24681                     r = this.doc.selection.createRange();
24682                     if(r){
24683                         var target = r.parentElement();
24684                         if(!target || target.tagName.toLowerCase() != 'li'){
24685                             e.stopEvent();
24686                             r.pasteHTML('<br />');
24687                             r.collapse(false);
24688                             r.select();
24689                         }
24690                     }
24691                 }
24692                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24693                     this.cleanUpPaste.defer(100, this);
24694                     return;
24695                 }
24696                 
24697                 
24698             };
24699         }else if(Roo.isOpera){
24700             return function(e){
24701                 var k = e.getKey();
24702                 if(k == e.TAB){
24703                     e.stopEvent();
24704                     this.win.focus();
24705                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24706                     this.deferFocus();
24707                 }
24708                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24709                     this.cleanUpPaste.defer(100, this);
24710                     return;
24711                 }
24712                 
24713             };
24714         }else if(Roo.isSafari){
24715             return function(e){
24716                 var k = e.getKey();
24717                 
24718                 if(k == e.TAB){
24719                     e.stopEvent();
24720                     this.execCmd('InsertText','\t');
24721                     this.deferFocus();
24722                     return;
24723                 }
24724                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24725                     this.cleanUpPaste.defer(100, this);
24726                     return;
24727                 }
24728                 
24729              };
24730         }
24731     }(),
24732     
24733     getAllAncestors: function()
24734     {
24735         var p = this.getSelectedNode();
24736         var a = [];
24737         if (!p) {
24738             a.push(p); // push blank onto stack..
24739             p = this.getParentElement();
24740         }
24741         
24742         
24743         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24744             a.push(p);
24745             p = p.parentNode;
24746         }
24747         a.push(this.doc.body);
24748         return a;
24749     },
24750     lastSel : false,
24751     lastSelNode : false,
24752     
24753     
24754     getSelection : function() 
24755     {
24756         this.assignDocWin();
24757         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24758     },
24759     
24760     getSelectedNode: function() 
24761     {
24762         // this may only work on Gecko!!!
24763         
24764         // should we cache this!!!!
24765         
24766         
24767         
24768          
24769         var range = this.createRange(this.getSelection()).cloneRange();
24770         
24771         if (Roo.isIE) {
24772             var parent = range.parentElement();
24773             while (true) {
24774                 var testRange = range.duplicate();
24775                 testRange.moveToElementText(parent);
24776                 if (testRange.inRange(range)) {
24777                     break;
24778                 }
24779                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24780                     break;
24781                 }
24782                 parent = parent.parentElement;
24783             }
24784             return parent;
24785         }
24786         
24787         // is ancestor a text element.
24788         var ac =  range.commonAncestorContainer;
24789         if (ac.nodeType == 3) {
24790             ac = ac.parentNode;
24791         }
24792         
24793         var ar = ac.childNodes;
24794          
24795         var nodes = [];
24796         var other_nodes = [];
24797         var has_other_nodes = false;
24798         for (var i=0;i<ar.length;i++) {
24799             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24800                 continue;
24801             }
24802             // fullly contained node.
24803             
24804             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24805                 nodes.push(ar[i]);
24806                 continue;
24807             }
24808             
24809             // probably selected..
24810             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24811                 other_nodes.push(ar[i]);
24812                 continue;
24813             }
24814             // outer..
24815             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24816                 continue;
24817             }
24818             
24819             
24820             has_other_nodes = true;
24821         }
24822         if (!nodes.length && other_nodes.length) {
24823             nodes= other_nodes;
24824         }
24825         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24826             return false;
24827         }
24828         
24829         return nodes[0];
24830     },
24831     createRange: function(sel)
24832     {
24833         // this has strange effects when using with 
24834         // top toolbar - not sure if it's a great idea.
24835         //this.editor.contentWindow.focus();
24836         if (typeof sel != "undefined") {
24837             try {
24838                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24839             } catch(e) {
24840                 return this.doc.createRange();
24841             }
24842         } else {
24843             return this.doc.createRange();
24844         }
24845     },
24846     getParentElement: function()
24847     {
24848         
24849         this.assignDocWin();
24850         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24851         
24852         var range = this.createRange(sel);
24853          
24854         try {
24855             var p = range.commonAncestorContainer;
24856             while (p.nodeType == 3) { // text node
24857                 p = p.parentNode;
24858             }
24859             return p;
24860         } catch (e) {
24861             return null;
24862         }
24863     
24864     },
24865     /***
24866      *
24867      * Range intersection.. the hard stuff...
24868      *  '-1' = before
24869      *  '0' = hits..
24870      *  '1' = after.
24871      *         [ -- selected range --- ]
24872      *   [fail]                        [fail]
24873      *
24874      *    basically..
24875      *      if end is before start or  hits it. fail.
24876      *      if start is after end or hits it fail.
24877      *
24878      *   if either hits (but other is outside. - then it's not 
24879      *   
24880      *    
24881      **/
24882     
24883     
24884     // @see http://www.thismuchiknow.co.uk/?p=64.
24885     rangeIntersectsNode : function(range, node)
24886     {
24887         var nodeRange = node.ownerDocument.createRange();
24888         try {
24889             nodeRange.selectNode(node);
24890         } catch (e) {
24891             nodeRange.selectNodeContents(node);
24892         }
24893     
24894         var rangeStartRange = range.cloneRange();
24895         rangeStartRange.collapse(true);
24896     
24897         var rangeEndRange = range.cloneRange();
24898         rangeEndRange.collapse(false);
24899     
24900         var nodeStartRange = nodeRange.cloneRange();
24901         nodeStartRange.collapse(true);
24902     
24903         var nodeEndRange = nodeRange.cloneRange();
24904         nodeEndRange.collapse(false);
24905     
24906         return rangeStartRange.compareBoundaryPoints(
24907                  Range.START_TO_START, nodeEndRange) == -1 &&
24908                rangeEndRange.compareBoundaryPoints(
24909                  Range.START_TO_START, nodeStartRange) == 1;
24910         
24911          
24912     },
24913     rangeCompareNode : function(range, node)
24914     {
24915         var nodeRange = node.ownerDocument.createRange();
24916         try {
24917             nodeRange.selectNode(node);
24918         } catch (e) {
24919             nodeRange.selectNodeContents(node);
24920         }
24921         
24922         
24923         range.collapse(true);
24924     
24925         nodeRange.collapse(true);
24926      
24927         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24928         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24929          
24930         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24931         
24932         var nodeIsBefore   =  ss == 1;
24933         var nodeIsAfter    = ee == -1;
24934         
24935         if (nodeIsBefore && nodeIsAfter) {
24936             return 0; // outer
24937         }
24938         if (!nodeIsBefore && nodeIsAfter) {
24939             return 1; //right trailed.
24940         }
24941         
24942         if (nodeIsBefore && !nodeIsAfter) {
24943             return 2;  // left trailed.
24944         }
24945         // fully contined.
24946         return 3;
24947     },
24948
24949     // private? - in a new class?
24950     cleanUpPaste :  function()
24951     {
24952         // cleans up the whole document..
24953         Roo.log('cleanuppaste');
24954         
24955         this.cleanUpChildren(this.doc.body);
24956         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24957         if (clean != this.doc.body.innerHTML) {
24958             this.doc.body.innerHTML = clean;
24959         }
24960         
24961     },
24962     
24963     cleanWordChars : function(input) {// change the chars to hex code
24964         var he = Roo.HtmlEditorCore;
24965         
24966         var output = input;
24967         Roo.each(he.swapCodes, function(sw) { 
24968             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24969             
24970             output = output.replace(swapper, sw[1]);
24971         });
24972         
24973         return output;
24974     },
24975     
24976     
24977     cleanUpChildren : function (n)
24978     {
24979         if (!n.childNodes.length) {
24980             return;
24981         }
24982         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24983            this.cleanUpChild(n.childNodes[i]);
24984         }
24985     },
24986     
24987     
24988         
24989     
24990     cleanUpChild : function (node)
24991     {
24992         var ed = this;
24993         //console.log(node);
24994         if (node.nodeName == "#text") {
24995             // clean up silly Windows -- stuff?
24996             return; 
24997         }
24998         if (node.nodeName == "#comment") {
24999             node.parentNode.removeChild(node);
25000             // clean up silly Windows -- stuff?
25001             return; 
25002         }
25003         var lcname = node.tagName.toLowerCase();
25004         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25005         // whitelist of tags..
25006         
25007         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25008             // remove node.
25009             node.parentNode.removeChild(node);
25010             return;
25011             
25012         }
25013         
25014         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25015         
25016         // spans with no attributes - just remove them..
25017         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25018             remove_keep_children = true;
25019         }
25020         
25021         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25022         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25023         
25024         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25025         //    remove_keep_children = true;
25026         //}
25027         
25028         if (remove_keep_children) {
25029             this.cleanUpChildren(node);
25030             // inserts everything just before this node...
25031             while (node.childNodes.length) {
25032                 var cn = node.childNodes[0];
25033                 node.removeChild(cn);
25034                 node.parentNode.insertBefore(cn, node);
25035             }
25036             node.parentNode.removeChild(node);
25037             return;
25038         }
25039         
25040         if (!node.attributes || !node.attributes.length) {
25041             
25042           
25043             
25044             
25045             this.cleanUpChildren(node);
25046             return;
25047         }
25048         
25049         function cleanAttr(n,v)
25050         {
25051             
25052             if (v.match(/^\./) || v.match(/^\//)) {
25053                 return;
25054             }
25055             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25056                 return;
25057             }
25058             if (v.match(/^#/)) {
25059                 return;
25060             }
25061             if (v.match(/^\{/)) { // allow template editing.
25062                 return;
25063             }
25064 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25065             node.removeAttribute(n);
25066             
25067         }
25068         
25069         var cwhite = this.cwhite;
25070         var cblack = this.cblack;
25071             
25072         function cleanStyle(n,v)
25073         {
25074             if (v.match(/expression/)) { //XSS?? should we even bother..
25075                 node.removeAttribute(n);
25076                 return;
25077             }
25078             
25079             var parts = v.split(/;/);
25080             var clean = [];
25081             
25082             Roo.each(parts, function(p) {
25083                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25084                 if (!p.length) {
25085                     return true;
25086                 }
25087                 var l = p.split(':').shift().replace(/\s+/g,'');
25088                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25089                 
25090                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25091 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25092                     //node.removeAttribute(n);
25093                     return true;
25094                 }
25095                 //Roo.log()
25096                 // only allow 'c whitelisted system attributes'
25097                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25098 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25099                     //node.removeAttribute(n);
25100                     return true;
25101                 }
25102                 
25103                 
25104                  
25105                 
25106                 clean.push(p);
25107                 return true;
25108             });
25109             if (clean.length) { 
25110                 node.setAttribute(n, clean.join(';'));
25111             } else {
25112                 node.removeAttribute(n);
25113             }
25114             
25115         }
25116         
25117         
25118         for (var i = node.attributes.length-1; i > -1 ; i--) {
25119             var a = node.attributes[i];
25120             //console.log(a);
25121             
25122             if (a.name.toLowerCase().substr(0,2)=='on')  {
25123                 node.removeAttribute(a.name);
25124                 continue;
25125             }
25126             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25127                 node.removeAttribute(a.name);
25128                 continue;
25129             }
25130             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25131                 cleanAttr(a.name,a.value); // fixme..
25132                 continue;
25133             }
25134             if (a.name == 'style') {
25135                 cleanStyle(a.name,a.value);
25136                 continue;
25137             }
25138             /// clean up MS crap..
25139             // tecnically this should be a list of valid class'es..
25140             
25141             
25142             if (a.name == 'class') {
25143                 if (a.value.match(/^Mso/)) {
25144                     node.removeAttribute('class');
25145                 }
25146                 
25147                 if (a.value.match(/^body$/)) {
25148                     node.removeAttribute('class');
25149                 }
25150                 continue;
25151             }
25152             
25153             // style cleanup!?
25154             // class cleanup?
25155             
25156         }
25157         
25158         
25159         this.cleanUpChildren(node);
25160         
25161         
25162     },
25163     
25164     /**
25165      * Clean up MS wordisms...
25166      */
25167     cleanWord : function(node)
25168     {
25169         if (!node) {
25170             this.cleanWord(this.doc.body);
25171             return;
25172         }
25173         
25174         if(
25175                 node.nodeName == 'SPAN' &&
25176                 !node.hasAttributes() &&
25177                 node.childNodes.length == 1 &&
25178                 node.firstChild.nodeName == "#text"  
25179         ) {
25180             var textNode = node.firstChild;
25181             node.removeChild(textNode);
25182             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25183                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25184             }
25185             node.parentNode.insertBefore(textNode, node);
25186             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25187                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25188             }
25189             node.parentNode.removeChild(node);
25190         }
25191         
25192         if (node.nodeName == "#text") {
25193             // clean up silly Windows -- stuff?
25194             return; 
25195         }
25196         if (node.nodeName == "#comment") {
25197             node.parentNode.removeChild(node);
25198             // clean up silly Windows -- stuff?
25199             return; 
25200         }
25201         
25202         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25203             node.parentNode.removeChild(node);
25204             return;
25205         }
25206         //Roo.log(node.tagName);
25207         // remove - but keep children..
25208         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25209             //Roo.log('-- removed');
25210             while (node.childNodes.length) {
25211                 var cn = node.childNodes[0];
25212                 node.removeChild(cn);
25213                 node.parentNode.insertBefore(cn, node);
25214                 // move node to parent - and clean it..
25215                 this.cleanWord(cn);
25216             }
25217             node.parentNode.removeChild(node);
25218             /// no need to iterate chidlren = it's got none..
25219             //this.iterateChildren(node, this.cleanWord);
25220             return;
25221         }
25222         // clean styles
25223         if (node.className.length) {
25224             
25225             var cn = node.className.split(/\W+/);
25226             var cna = [];
25227             Roo.each(cn, function(cls) {
25228                 if (cls.match(/Mso[a-zA-Z]+/)) {
25229                     return;
25230                 }
25231                 cna.push(cls);
25232             });
25233             node.className = cna.length ? cna.join(' ') : '';
25234             if (!cna.length) {
25235                 node.removeAttribute("class");
25236             }
25237         }
25238         
25239         if (node.hasAttribute("lang")) {
25240             node.removeAttribute("lang");
25241         }
25242         
25243         if (node.hasAttribute("style")) {
25244             
25245             var styles = node.getAttribute("style").split(";");
25246             var nstyle = [];
25247             Roo.each(styles, function(s) {
25248                 if (!s.match(/:/)) {
25249                     return;
25250                 }
25251                 var kv = s.split(":");
25252                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25253                     return;
25254                 }
25255                 // what ever is left... we allow.
25256                 nstyle.push(s);
25257             });
25258             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25259             if (!nstyle.length) {
25260                 node.removeAttribute('style');
25261             }
25262         }
25263         this.iterateChildren(node, this.cleanWord);
25264         
25265         
25266         
25267     },
25268     /**
25269      * iterateChildren of a Node, calling fn each time, using this as the scole..
25270      * @param {DomNode} node node to iterate children of.
25271      * @param {Function} fn method of this class to call on each item.
25272      */
25273     iterateChildren : function(node, fn)
25274     {
25275         if (!node.childNodes.length) {
25276                 return;
25277         }
25278         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25279            fn.call(this, node.childNodes[i])
25280         }
25281     },
25282     
25283     
25284     /**
25285      * cleanTableWidths.
25286      *
25287      * Quite often pasting from word etc.. results in tables with column and widths.
25288      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25289      *
25290      */
25291     cleanTableWidths : function(node)
25292     {
25293          
25294          
25295         if (!node) {
25296             this.cleanTableWidths(this.doc.body);
25297             return;
25298         }
25299         
25300         // ignore list...
25301         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25302             return; 
25303         }
25304         Roo.log(node.tagName);
25305         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25306             this.iterateChildren(node, this.cleanTableWidths);
25307             return;
25308         }
25309         if (node.hasAttribute('width')) {
25310             node.removeAttribute('width');
25311         }
25312         
25313          
25314         if (node.hasAttribute("style")) {
25315             // pretty basic...
25316             
25317             var styles = node.getAttribute("style").split(";");
25318             var nstyle = [];
25319             Roo.each(styles, function(s) {
25320                 if (!s.match(/:/)) {
25321                     return;
25322                 }
25323                 var kv = s.split(":");
25324                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25325                     return;
25326                 }
25327                 // what ever is left... we allow.
25328                 nstyle.push(s);
25329             });
25330             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25331             if (!nstyle.length) {
25332                 node.removeAttribute('style');
25333             }
25334         }
25335         
25336         this.iterateChildren(node, this.cleanTableWidths);
25337         
25338         
25339     },
25340     
25341     
25342     
25343     
25344     domToHTML : function(currentElement, depth, nopadtext) {
25345         
25346         depth = depth || 0;
25347         nopadtext = nopadtext || false;
25348     
25349         if (!currentElement) {
25350             return this.domToHTML(this.doc.body);
25351         }
25352         
25353         //Roo.log(currentElement);
25354         var j;
25355         var allText = false;
25356         var nodeName = currentElement.nodeName;
25357         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25358         
25359         if  (nodeName == '#text') {
25360             
25361             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25362         }
25363         
25364         
25365         var ret = '';
25366         if (nodeName != 'BODY') {
25367              
25368             var i = 0;
25369             // Prints the node tagName, such as <A>, <IMG>, etc
25370             if (tagName) {
25371                 var attr = [];
25372                 for(i = 0; i < currentElement.attributes.length;i++) {
25373                     // quoting?
25374                     var aname = currentElement.attributes.item(i).name;
25375                     if (!currentElement.attributes.item(i).value.length) {
25376                         continue;
25377                     }
25378                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25379                 }
25380                 
25381                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25382             } 
25383             else {
25384                 
25385                 // eack
25386             }
25387         } else {
25388             tagName = false;
25389         }
25390         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25391             return ret;
25392         }
25393         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25394             nopadtext = true;
25395         }
25396         
25397         
25398         // Traverse the tree
25399         i = 0;
25400         var currentElementChild = currentElement.childNodes.item(i);
25401         var allText = true;
25402         var innerHTML  = '';
25403         lastnode = '';
25404         while (currentElementChild) {
25405             // Formatting code (indent the tree so it looks nice on the screen)
25406             var nopad = nopadtext;
25407             if (lastnode == 'SPAN') {
25408                 nopad  = true;
25409             }
25410             // text
25411             if  (currentElementChild.nodeName == '#text') {
25412                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25413                 toadd = nopadtext ? toadd : toadd.trim();
25414                 if (!nopad && toadd.length > 80) {
25415                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25416                 }
25417                 innerHTML  += toadd;
25418                 
25419                 i++;
25420                 currentElementChild = currentElement.childNodes.item(i);
25421                 lastNode = '';
25422                 continue;
25423             }
25424             allText = false;
25425             
25426             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25427                 
25428             // Recursively traverse the tree structure of the child node
25429             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25430             lastnode = currentElementChild.nodeName;
25431             i++;
25432             currentElementChild=currentElement.childNodes.item(i);
25433         }
25434         
25435         ret += innerHTML;
25436         
25437         if (!allText) {
25438                 // The remaining code is mostly for formatting the tree
25439             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25440         }
25441         
25442         
25443         if (tagName) {
25444             ret+= "</"+tagName+">";
25445         }
25446         return ret;
25447         
25448     },
25449         
25450     applyBlacklists : function()
25451     {
25452         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25453         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25454         
25455         this.white = [];
25456         this.black = [];
25457         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25458             if (b.indexOf(tag) > -1) {
25459                 return;
25460             }
25461             this.white.push(tag);
25462             
25463         }, this);
25464         
25465         Roo.each(w, function(tag) {
25466             if (b.indexOf(tag) > -1) {
25467                 return;
25468             }
25469             if (this.white.indexOf(tag) > -1) {
25470                 return;
25471             }
25472             this.white.push(tag);
25473             
25474         }, this);
25475         
25476         
25477         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25478             if (w.indexOf(tag) > -1) {
25479                 return;
25480             }
25481             this.black.push(tag);
25482             
25483         }, this);
25484         
25485         Roo.each(b, function(tag) {
25486             if (w.indexOf(tag) > -1) {
25487                 return;
25488             }
25489             if (this.black.indexOf(tag) > -1) {
25490                 return;
25491             }
25492             this.black.push(tag);
25493             
25494         }, this);
25495         
25496         
25497         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25498         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25499         
25500         this.cwhite = [];
25501         this.cblack = [];
25502         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25503             if (b.indexOf(tag) > -1) {
25504                 return;
25505             }
25506             this.cwhite.push(tag);
25507             
25508         }, this);
25509         
25510         Roo.each(w, function(tag) {
25511             if (b.indexOf(tag) > -1) {
25512                 return;
25513             }
25514             if (this.cwhite.indexOf(tag) > -1) {
25515                 return;
25516             }
25517             this.cwhite.push(tag);
25518             
25519         }, this);
25520         
25521         
25522         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25523             if (w.indexOf(tag) > -1) {
25524                 return;
25525             }
25526             this.cblack.push(tag);
25527             
25528         }, this);
25529         
25530         Roo.each(b, function(tag) {
25531             if (w.indexOf(tag) > -1) {
25532                 return;
25533             }
25534             if (this.cblack.indexOf(tag) > -1) {
25535                 return;
25536             }
25537             this.cblack.push(tag);
25538             
25539         }, this);
25540     },
25541     
25542     setStylesheets : function(stylesheets)
25543     {
25544         if(typeof(stylesheets) == 'string'){
25545             Roo.get(this.iframe.contentDocument.head).createChild({
25546                 tag : 'link',
25547                 rel : 'stylesheet',
25548                 type : 'text/css',
25549                 href : stylesheets
25550             });
25551             
25552             return;
25553         }
25554         var _this = this;
25555      
25556         Roo.each(stylesheets, function(s) {
25557             if(!s.length){
25558                 return;
25559             }
25560             
25561             Roo.get(_this.iframe.contentDocument.head).createChild({
25562                 tag : 'link',
25563                 rel : 'stylesheet',
25564                 type : 'text/css',
25565                 href : s
25566             });
25567         });
25568
25569         
25570     },
25571     
25572     removeStylesheets : function()
25573     {
25574         var _this = this;
25575         
25576         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25577             s.remove();
25578         });
25579     },
25580     
25581     setStyle : function(style)
25582     {
25583         Roo.get(this.iframe.contentDocument.head).createChild({
25584             tag : 'style',
25585             type : 'text/css',
25586             html : style
25587         });
25588
25589         return;
25590     }
25591     
25592     // hide stuff that is not compatible
25593     /**
25594      * @event blur
25595      * @hide
25596      */
25597     /**
25598      * @event change
25599      * @hide
25600      */
25601     /**
25602      * @event focus
25603      * @hide
25604      */
25605     /**
25606      * @event specialkey
25607      * @hide
25608      */
25609     /**
25610      * @cfg {String} fieldClass @hide
25611      */
25612     /**
25613      * @cfg {String} focusClass @hide
25614      */
25615     /**
25616      * @cfg {String} autoCreate @hide
25617      */
25618     /**
25619      * @cfg {String} inputType @hide
25620      */
25621     /**
25622      * @cfg {String} invalidClass @hide
25623      */
25624     /**
25625      * @cfg {String} invalidText @hide
25626      */
25627     /**
25628      * @cfg {String} msgFx @hide
25629      */
25630     /**
25631      * @cfg {String} validateOnBlur @hide
25632      */
25633 });
25634
25635 Roo.HtmlEditorCore.white = [
25636         'area', 'br', 'img', 'input', 'hr', 'wbr',
25637         
25638        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25639        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25640        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25641        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25642        'table',   'ul',         'xmp', 
25643        
25644        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25645       'thead',   'tr', 
25646      
25647       'dir', 'menu', 'ol', 'ul', 'dl',
25648        
25649       'embed',  'object'
25650 ];
25651
25652
25653 Roo.HtmlEditorCore.black = [
25654     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25655         'applet', // 
25656         'base',   'basefont', 'bgsound', 'blink',  'body', 
25657         'frame',  'frameset', 'head',    'html',   'ilayer', 
25658         'iframe', 'layer',  'link',     'meta',    'object',   
25659         'script', 'style' ,'title',  'xml' // clean later..
25660 ];
25661 Roo.HtmlEditorCore.clean = [
25662     'script', 'style', 'title', 'xml'
25663 ];
25664 Roo.HtmlEditorCore.remove = [
25665     'font'
25666 ];
25667 // attributes..
25668
25669 Roo.HtmlEditorCore.ablack = [
25670     'on'
25671 ];
25672     
25673 Roo.HtmlEditorCore.aclean = [ 
25674     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25675 ];
25676
25677 // protocols..
25678 Roo.HtmlEditorCore.pwhite= [
25679         'http',  'https',  'mailto'
25680 ];
25681
25682 // white listed style attributes.
25683 Roo.HtmlEditorCore.cwhite= [
25684       //  'text-align', /// default is to allow most things..
25685       
25686          
25687 //        'font-size'//??
25688 ];
25689
25690 // black listed style attributes.
25691 Roo.HtmlEditorCore.cblack= [
25692       //  'font-size' -- this can be set by the project 
25693 ];
25694
25695
25696 Roo.HtmlEditorCore.swapCodes   =[ 
25697     [    8211, "--" ], 
25698     [    8212, "--" ], 
25699     [    8216,  "'" ],  
25700     [    8217, "'" ],  
25701     [    8220, '"' ],  
25702     [    8221, '"' ],  
25703     [    8226, "*" ],  
25704     [    8230, "..." ]
25705 ]; 
25706
25707     /*
25708  * - LGPL
25709  *
25710  * HtmlEditor
25711  * 
25712  */
25713
25714 /**
25715  * @class Roo.bootstrap.HtmlEditor
25716  * @extends Roo.bootstrap.TextArea
25717  * Bootstrap HtmlEditor class
25718
25719  * @constructor
25720  * Create a new HtmlEditor
25721  * @param {Object} config The config object
25722  */
25723
25724 Roo.bootstrap.HtmlEditor = function(config){
25725     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25726     if (!this.toolbars) {
25727         this.toolbars = [];
25728     }
25729     
25730     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25731     this.addEvents({
25732             /**
25733              * @event initialize
25734              * Fires when the editor is fully initialized (including the iframe)
25735              * @param {HtmlEditor} this
25736              */
25737             initialize: true,
25738             /**
25739              * @event activate
25740              * Fires when the editor is first receives the focus. Any insertion must wait
25741              * until after this event.
25742              * @param {HtmlEditor} this
25743              */
25744             activate: true,
25745              /**
25746              * @event beforesync
25747              * Fires before the textarea is updated with content from the editor iframe. Return false
25748              * to cancel the sync.
25749              * @param {HtmlEditor} this
25750              * @param {String} html
25751              */
25752             beforesync: true,
25753              /**
25754              * @event beforepush
25755              * Fires before the iframe editor is updated with content from the textarea. Return false
25756              * to cancel the push.
25757              * @param {HtmlEditor} this
25758              * @param {String} html
25759              */
25760             beforepush: true,
25761              /**
25762              * @event sync
25763              * Fires when the textarea is updated with content from the editor iframe.
25764              * @param {HtmlEditor} this
25765              * @param {String} html
25766              */
25767             sync: true,
25768              /**
25769              * @event push
25770              * Fires when the iframe editor is updated with content from the textarea.
25771              * @param {HtmlEditor} this
25772              * @param {String} html
25773              */
25774             push: true,
25775              /**
25776              * @event editmodechange
25777              * Fires when the editor switches edit modes
25778              * @param {HtmlEditor} this
25779              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25780              */
25781             editmodechange: true,
25782             /**
25783              * @event editorevent
25784              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25785              * @param {HtmlEditor} this
25786              */
25787             editorevent: true,
25788             /**
25789              * @event firstfocus
25790              * Fires when on first focus - needed by toolbars..
25791              * @param {HtmlEditor} this
25792              */
25793             firstfocus: true,
25794             /**
25795              * @event autosave
25796              * Auto save the htmlEditor value as a file into Events
25797              * @param {HtmlEditor} this
25798              */
25799             autosave: true,
25800             /**
25801              * @event savedpreview
25802              * preview the saved version of htmlEditor
25803              * @param {HtmlEditor} this
25804              */
25805             savedpreview: true
25806         });
25807 };
25808
25809
25810 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25811     
25812     
25813       /**
25814      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25815      */
25816     toolbars : false,
25817     
25818      /**
25819     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25820     */
25821     btns : [],
25822    
25823      /**
25824      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25825      *                        Roo.resizable.
25826      */
25827     resizable : false,
25828      /**
25829      * @cfg {Number} height (in pixels)
25830      */   
25831     height: 300,
25832    /**
25833      * @cfg {Number} width (in pixels)
25834      */   
25835     width: false,
25836     
25837     /**
25838      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25839      * 
25840      */
25841     stylesheets: false,
25842     
25843     // id of frame..
25844     frameId: false,
25845     
25846     // private properties
25847     validationEvent : false,
25848     deferHeight: true,
25849     initialized : false,
25850     activated : false,
25851     
25852     onFocus : Roo.emptyFn,
25853     iframePad:3,
25854     hideMode:'offsets',
25855     
25856     tbContainer : false,
25857     
25858     bodyCls : '',
25859     
25860     toolbarContainer :function() {
25861         return this.wrap.select('.x-html-editor-tb',true).first();
25862     },
25863
25864     /**
25865      * Protected method that will not generally be called directly. It
25866      * is called when the editor creates its toolbar. Override this method if you need to
25867      * add custom toolbar buttons.
25868      * @param {HtmlEditor} editor
25869      */
25870     createToolbar : function(){
25871         Roo.log('renewing');
25872         Roo.log("create toolbars");
25873         
25874         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25875         this.toolbars[0].render(this.toolbarContainer());
25876         
25877         return;
25878         
25879 //        if (!editor.toolbars || !editor.toolbars.length) {
25880 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25881 //        }
25882 //        
25883 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25884 //            editor.toolbars[i] = Roo.factory(
25885 //                    typeof(editor.toolbars[i]) == 'string' ?
25886 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25887 //                Roo.bootstrap.HtmlEditor);
25888 //            editor.toolbars[i].init(editor);
25889 //        }
25890     },
25891
25892      
25893     // private
25894     onRender : function(ct, position)
25895     {
25896        // Roo.log("Call onRender: " + this.xtype);
25897         var _t = this;
25898         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25899       
25900         this.wrap = this.inputEl().wrap({
25901             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25902         });
25903         
25904         this.editorcore.onRender(ct, position);
25905          
25906         if (this.resizable) {
25907             this.resizeEl = new Roo.Resizable(this.wrap, {
25908                 pinned : true,
25909                 wrap: true,
25910                 dynamic : true,
25911                 minHeight : this.height,
25912                 height: this.height,
25913                 handles : this.resizable,
25914                 width: this.width,
25915                 listeners : {
25916                     resize : function(r, w, h) {
25917                         _t.onResize(w,h); // -something
25918                     }
25919                 }
25920             });
25921             
25922         }
25923         this.createToolbar(this);
25924        
25925         
25926         if(!this.width && this.resizable){
25927             this.setSize(this.wrap.getSize());
25928         }
25929         if (this.resizeEl) {
25930             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25931             // should trigger onReize..
25932         }
25933         
25934     },
25935
25936     // private
25937     onResize : function(w, h)
25938     {
25939         Roo.log('resize: ' +w + ',' + h );
25940         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25941         var ew = false;
25942         var eh = false;
25943         
25944         if(this.inputEl() ){
25945             if(typeof w == 'number'){
25946                 var aw = w - this.wrap.getFrameWidth('lr');
25947                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25948                 ew = aw;
25949             }
25950             if(typeof h == 'number'){
25951                  var tbh = -11;  // fixme it needs to tool bar size!
25952                 for (var i =0; i < this.toolbars.length;i++) {
25953                     // fixme - ask toolbars for heights?
25954                     tbh += this.toolbars[i].el.getHeight();
25955                     //if (this.toolbars[i].footer) {
25956                     //    tbh += this.toolbars[i].footer.el.getHeight();
25957                     //}
25958                 }
25959               
25960                 
25961                 
25962                 
25963                 
25964                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25965                 ah -= 5; // knock a few pixes off for look..
25966                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25967                 var eh = ah;
25968             }
25969         }
25970         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25971         this.editorcore.onResize(ew,eh);
25972         
25973     },
25974
25975     /**
25976      * Toggles the editor between standard and source edit mode.
25977      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25978      */
25979     toggleSourceEdit : function(sourceEditMode)
25980     {
25981         this.editorcore.toggleSourceEdit(sourceEditMode);
25982         
25983         if(this.editorcore.sourceEditMode){
25984             Roo.log('editor - showing textarea');
25985             
25986 //            Roo.log('in');
25987 //            Roo.log(this.syncValue());
25988             this.syncValue();
25989             this.inputEl().removeClass(['hide', 'x-hidden']);
25990             this.inputEl().dom.removeAttribute('tabIndex');
25991             this.inputEl().focus();
25992         }else{
25993             Roo.log('editor - hiding textarea');
25994 //            Roo.log('out')
25995 //            Roo.log(this.pushValue()); 
25996             this.pushValue();
25997             
25998             this.inputEl().addClass(['hide', 'x-hidden']);
25999             this.inputEl().dom.setAttribute('tabIndex', -1);
26000             //this.deferFocus();
26001         }
26002          
26003         if(this.resizable){
26004             this.setSize(this.wrap.getSize());
26005         }
26006         
26007         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26008     },
26009  
26010     // private (for BoxComponent)
26011     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26012
26013     // private (for BoxComponent)
26014     getResizeEl : function(){
26015         return this.wrap;
26016     },
26017
26018     // private (for BoxComponent)
26019     getPositionEl : function(){
26020         return this.wrap;
26021     },
26022
26023     // private
26024     initEvents : function(){
26025         this.originalValue = this.getValue();
26026     },
26027
26028 //    /**
26029 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26030 //     * @method
26031 //     */
26032 //    markInvalid : Roo.emptyFn,
26033 //    /**
26034 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26035 //     * @method
26036 //     */
26037 //    clearInvalid : Roo.emptyFn,
26038
26039     setValue : function(v){
26040         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26041         this.editorcore.pushValue();
26042     },
26043
26044      
26045     // private
26046     deferFocus : function(){
26047         this.focus.defer(10, this);
26048     },
26049
26050     // doc'ed in Field
26051     focus : function(){
26052         this.editorcore.focus();
26053         
26054     },
26055       
26056
26057     // private
26058     onDestroy : function(){
26059         
26060         
26061         
26062         if(this.rendered){
26063             
26064             for (var i =0; i < this.toolbars.length;i++) {
26065                 // fixme - ask toolbars for heights?
26066                 this.toolbars[i].onDestroy();
26067             }
26068             
26069             this.wrap.dom.innerHTML = '';
26070             this.wrap.remove();
26071         }
26072     },
26073
26074     // private
26075     onFirstFocus : function(){
26076         //Roo.log("onFirstFocus");
26077         this.editorcore.onFirstFocus();
26078          for (var i =0; i < this.toolbars.length;i++) {
26079             this.toolbars[i].onFirstFocus();
26080         }
26081         
26082     },
26083     
26084     // private
26085     syncValue : function()
26086     {   
26087         this.editorcore.syncValue();
26088     },
26089     
26090     pushValue : function()
26091     {   
26092         this.editorcore.pushValue();
26093     }
26094      
26095     
26096     // hide stuff that is not compatible
26097     /**
26098      * @event blur
26099      * @hide
26100      */
26101     /**
26102      * @event change
26103      * @hide
26104      */
26105     /**
26106      * @event focus
26107      * @hide
26108      */
26109     /**
26110      * @event specialkey
26111      * @hide
26112      */
26113     /**
26114      * @cfg {String} fieldClass @hide
26115      */
26116     /**
26117      * @cfg {String} focusClass @hide
26118      */
26119     /**
26120      * @cfg {String} autoCreate @hide
26121      */
26122     /**
26123      * @cfg {String} inputType @hide
26124      */
26125      
26126     /**
26127      * @cfg {String} invalidText @hide
26128      */
26129     /**
26130      * @cfg {String} msgFx @hide
26131      */
26132     /**
26133      * @cfg {String} validateOnBlur @hide
26134      */
26135 });
26136  
26137     
26138    
26139    
26140    
26141       
26142 Roo.namespace('Roo.bootstrap.htmleditor');
26143 /**
26144  * @class Roo.bootstrap.HtmlEditorToolbar1
26145  * Basic Toolbar
26146  * 
26147  * @example
26148  * Usage:
26149  *
26150  new Roo.bootstrap.HtmlEditor({
26151     ....
26152     toolbars : [
26153         new Roo.bootstrap.HtmlEditorToolbar1({
26154             disable : { fonts: 1 , format: 1, ..., ... , ...],
26155             btns : [ .... ]
26156         })
26157     }
26158      
26159  * 
26160  * @cfg {Object} disable List of elements to disable..
26161  * @cfg {Array} btns List of additional buttons.
26162  * 
26163  * 
26164  * NEEDS Extra CSS? 
26165  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26166  */
26167  
26168 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26169 {
26170     
26171     Roo.apply(this, config);
26172     
26173     // default disabled, based on 'good practice'..
26174     this.disable = this.disable || {};
26175     Roo.applyIf(this.disable, {
26176         fontSize : true,
26177         colors : true,
26178         specialElements : true
26179     });
26180     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26181     
26182     this.editor = config.editor;
26183     this.editorcore = config.editor.editorcore;
26184     
26185     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26186     
26187     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26188     // dont call parent... till later.
26189 }
26190 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26191      
26192     bar : true,
26193     
26194     editor : false,
26195     editorcore : false,
26196     
26197     
26198     formats : [
26199         "p" ,  
26200         "h1","h2","h3","h4","h5","h6", 
26201         "pre", "code", 
26202         "abbr", "acronym", "address", "cite", "samp", "var",
26203         'div','span'
26204     ],
26205     
26206     onRender : function(ct, position)
26207     {
26208        // Roo.log("Call onRender: " + this.xtype);
26209         
26210        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26211        Roo.log(this.el);
26212        this.el.dom.style.marginBottom = '0';
26213        var _this = this;
26214        var editorcore = this.editorcore;
26215        var editor= this.editor;
26216        
26217        var children = [];
26218        var btn = function(id,cmd , toggle, handler, html){
26219        
26220             var  event = toggle ? 'toggle' : 'click';
26221        
26222             var a = {
26223                 size : 'sm',
26224                 xtype: 'Button',
26225                 xns: Roo.bootstrap,
26226                 //glyphicon : id,
26227                 fa: id,
26228                 cmd : id || cmd,
26229                 enableToggle:toggle !== false,
26230                 html : html || '',
26231                 pressed : toggle ? false : null,
26232                 listeners : {}
26233             };
26234             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26235                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26236             };
26237             children.push(a);
26238             return a;
26239        }
26240        
26241     //    var cb_box = function...
26242         
26243         var style = {
26244                 xtype: 'Button',
26245                 size : 'sm',
26246                 xns: Roo.bootstrap,
26247                 fa : 'font',
26248                 //html : 'submit'
26249                 menu : {
26250                     xtype: 'Menu',
26251                     xns: Roo.bootstrap,
26252                     items:  []
26253                 }
26254         };
26255         Roo.each(this.formats, function(f) {
26256             style.menu.items.push({
26257                 xtype :'MenuItem',
26258                 xns: Roo.bootstrap,
26259                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26260                 tagname : f,
26261                 listeners : {
26262                     click : function()
26263                     {
26264                         editorcore.insertTag(this.tagname);
26265                         editor.focus();
26266                     }
26267                 }
26268                 
26269             });
26270         });
26271         children.push(style);   
26272         
26273         btn('bold',false,true);
26274         btn('italic',false,true);
26275         btn('align-left', 'justifyleft',true);
26276         btn('align-center', 'justifycenter',true);
26277         btn('align-right' , 'justifyright',true);
26278         btn('link', false, false, function(btn) {
26279             //Roo.log("create link?");
26280             var url = prompt(this.createLinkText, this.defaultLinkValue);
26281             if(url && url != 'http:/'+'/'){
26282                 this.editorcore.relayCmd('createlink', url);
26283             }
26284         }),
26285         btn('list','insertunorderedlist',true);
26286         btn('pencil', false,true, function(btn){
26287                 Roo.log(this);
26288                 this.toggleSourceEdit(btn.pressed);
26289         });
26290         
26291         if (this.editor.btns.length > 0) {
26292             for (var i = 0; i<this.editor.btns.length; i++) {
26293                 children.push(this.editor.btns[i]);
26294             }
26295         }
26296         
26297         /*
26298         var cog = {
26299                 xtype: 'Button',
26300                 size : 'sm',
26301                 xns: Roo.bootstrap,
26302                 glyphicon : 'cog',
26303                 //html : 'submit'
26304                 menu : {
26305                     xtype: 'Menu',
26306                     xns: Roo.bootstrap,
26307                     items:  []
26308                 }
26309         };
26310         
26311         cog.menu.items.push({
26312             xtype :'MenuItem',
26313             xns: Roo.bootstrap,
26314             html : Clean styles,
26315             tagname : f,
26316             listeners : {
26317                 click : function()
26318                 {
26319                     editorcore.insertTag(this.tagname);
26320                     editor.focus();
26321                 }
26322             }
26323             
26324         });
26325        */
26326         
26327          
26328        this.xtype = 'NavSimplebar';
26329         
26330         for(var i=0;i< children.length;i++) {
26331             
26332             this.buttons.add(this.addxtypeChild(children[i]));
26333             
26334         }
26335         
26336         editor.on('editorevent', this.updateToolbar, this);
26337     },
26338     onBtnClick : function(id)
26339     {
26340        this.editorcore.relayCmd(id);
26341        this.editorcore.focus();
26342     },
26343     
26344     /**
26345      * Protected method that will not generally be called directly. It triggers
26346      * a toolbar update by reading the markup state of the current selection in the editor.
26347      */
26348     updateToolbar: function(){
26349
26350         if(!this.editorcore.activated){
26351             this.editor.onFirstFocus(); // is this neeed?
26352             return;
26353         }
26354
26355         var btns = this.buttons; 
26356         var doc = this.editorcore.doc;
26357         btns.get('bold').setActive(doc.queryCommandState('bold'));
26358         btns.get('italic').setActive(doc.queryCommandState('italic'));
26359         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26360         
26361         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26362         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26363         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26364         
26365         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26366         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26367          /*
26368         
26369         var ans = this.editorcore.getAllAncestors();
26370         if (this.formatCombo) {
26371             
26372             
26373             var store = this.formatCombo.store;
26374             this.formatCombo.setValue("");
26375             for (var i =0; i < ans.length;i++) {
26376                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26377                     // select it..
26378                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26379                     break;
26380                 }
26381             }
26382         }
26383         
26384         
26385         
26386         // hides menus... - so this cant be on a menu...
26387         Roo.bootstrap.MenuMgr.hideAll();
26388         */
26389         Roo.bootstrap.MenuMgr.hideAll();
26390         //this.editorsyncValue();
26391     },
26392     onFirstFocus: function() {
26393         this.buttons.each(function(item){
26394            item.enable();
26395         });
26396     },
26397     toggleSourceEdit : function(sourceEditMode){
26398         
26399           
26400         if(sourceEditMode){
26401             Roo.log("disabling buttons");
26402            this.buttons.each( function(item){
26403                 if(item.cmd != 'pencil'){
26404                     item.disable();
26405                 }
26406             });
26407           
26408         }else{
26409             Roo.log("enabling buttons");
26410             if(this.editorcore.initialized){
26411                 this.buttons.each( function(item){
26412                     item.enable();
26413                 });
26414             }
26415             
26416         }
26417         Roo.log("calling toggole on editor");
26418         // tell the editor that it's been pressed..
26419         this.editor.toggleSourceEdit(sourceEditMode);
26420        
26421     }
26422 });
26423
26424
26425
26426
26427  
26428 /*
26429  * - LGPL
26430  */
26431
26432 /**
26433  * @class Roo.bootstrap.Markdown
26434  * @extends Roo.bootstrap.TextArea
26435  * Bootstrap Showdown editable area
26436  * @cfg {string} content
26437  * 
26438  * @constructor
26439  * Create a new Showdown
26440  */
26441
26442 Roo.bootstrap.Markdown = function(config){
26443     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26444    
26445 };
26446
26447 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26448     
26449     editing :false,
26450     
26451     initEvents : function()
26452     {
26453         
26454         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26455         this.markdownEl = this.el.createChild({
26456             cls : 'roo-markdown-area'
26457         });
26458         this.inputEl().addClass('d-none');
26459         if (this.getValue() == '') {
26460             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26461             
26462         } else {
26463             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26464         }
26465         this.markdownEl.on('click', this.toggleTextEdit, this);
26466         this.on('blur', this.toggleTextEdit, this);
26467         this.on('specialkey', this.resizeTextArea, this);
26468     },
26469     
26470     toggleTextEdit : function()
26471     {
26472         var sh = this.markdownEl.getHeight();
26473         this.inputEl().addClass('d-none');
26474         this.markdownEl.addClass('d-none');
26475         if (!this.editing) {
26476             // show editor?
26477             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26478             this.inputEl().removeClass('d-none');
26479             this.inputEl().focus();
26480             this.editing = true;
26481             return;
26482         }
26483         // show showdown...
26484         this.updateMarkdown();
26485         this.markdownEl.removeClass('d-none');
26486         this.editing = false;
26487         return;
26488     },
26489     updateMarkdown : function()
26490     {
26491         if (this.getValue() == '') {
26492             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26493             return;
26494         }
26495  
26496         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26497     },
26498     
26499     resizeTextArea: function () {
26500         
26501         var sh = 100;
26502         Roo.log([sh, this.getValue().split("\n").length * 30]);
26503         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26504     },
26505     setValue : function(val)
26506     {
26507         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26508         if (!this.editing) {
26509             this.updateMarkdown();
26510         }
26511         
26512     },
26513     focus : function()
26514     {
26515         if (!this.editing) {
26516             this.toggleTextEdit();
26517         }
26518         
26519     }
26520
26521
26522 });
26523 /**
26524  * @class Roo.bootstrap.Table.AbstractSelectionModel
26525  * @extends Roo.util.Observable
26526  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26527  * implemented by descendant classes.  This class should not be directly instantiated.
26528  * @constructor
26529  */
26530 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26531     this.locked = false;
26532     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26533 };
26534
26535
26536 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26537     /** @ignore Called by the grid automatically. Do not call directly. */
26538     init : function(grid){
26539         this.grid = grid;
26540         this.initEvents();
26541     },
26542
26543     /**
26544      * Locks the selections.
26545      */
26546     lock : function(){
26547         this.locked = true;
26548     },
26549
26550     /**
26551      * Unlocks the selections.
26552      */
26553     unlock : function(){
26554         this.locked = false;
26555     },
26556
26557     /**
26558      * Returns true if the selections are locked.
26559      * @return {Boolean}
26560      */
26561     isLocked : function(){
26562         return this.locked;
26563     },
26564     
26565     
26566     initEvents : function ()
26567     {
26568         
26569     }
26570 });
26571 /**
26572  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26573  * @class Roo.bootstrap.Table.RowSelectionModel
26574  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26575  * It supports multiple selections and keyboard selection/navigation. 
26576  * @constructor
26577  * @param {Object} config
26578  */
26579
26580 Roo.bootstrap.Table.RowSelectionModel = function(config){
26581     Roo.apply(this, config);
26582     this.selections = new Roo.util.MixedCollection(false, function(o){
26583         return o.id;
26584     });
26585
26586     this.last = false;
26587     this.lastActive = false;
26588
26589     this.addEvents({
26590         /**
26591              * @event selectionchange
26592              * Fires when the selection changes
26593              * @param {SelectionModel} this
26594              */
26595             "selectionchange" : true,
26596         /**
26597              * @event afterselectionchange
26598              * Fires after the selection changes (eg. by key press or clicking)
26599              * @param {SelectionModel} this
26600              */
26601             "afterselectionchange" : true,
26602         /**
26603              * @event beforerowselect
26604              * Fires when a row is selected being selected, return false to cancel.
26605              * @param {SelectionModel} this
26606              * @param {Number} rowIndex The selected index
26607              * @param {Boolean} keepExisting False if other selections will be cleared
26608              */
26609             "beforerowselect" : true,
26610         /**
26611              * @event rowselect
26612              * Fires when a row is selected.
26613              * @param {SelectionModel} this
26614              * @param {Number} rowIndex The selected index
26615              * @param {Roo.data.Record} r The record
26616              */
26617             "rowselect" : true,
26618         /**
26619              * @event rowdeselect
26620              * Fires when a row is deselected.
26621              * @param {SelectionModel} this
26622              * @param {Number} rowIndex The selected index
26623              */
26624         "rowdeselect" : true
26625     });
26626     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26627     this.locked = false;
26628  };
26629
26630 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26631     /**
26632      * @cfg {Boolean} singleSelect
26633      * True to allow selection of only one row at a time (defaults to false)
26634      */
26635     singleSelect : false,
26636
26637     // private
26638     initEvents : function()
26639     {
26640
26641         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26642         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26643         //}else{ // allow click to work like normal
26644          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26645         //}
26646         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26647         this.grid.on("rowclick", this.handleMouseDown, this);
26648         
26649         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26650             "up" : function(e){
26651                 if(!e.shiftKey){
26652                     this.selectPrevious(e.shiftKey);
26653                 }else if(this.last !== false && this.lastActive !== false){
26654                     var last = this.last;
26655                     this.selectRange(this.last,  this.lastActive-1);
26656                     this.grid.getView().focusRow(this.lastActive);
26657                     if(last !== false){
26658                         this.last = last;
26659                     }
26660                 }else{
26661                     this.selectFirstRow();
26662                 }
26663                 this.fireEvent("afterselectionchange", this);
26664             },
26665             "down" : function(e){
26666                 if(!e.shiftKey){
26667                     this.selectNext(e.shiftKey);
26668                 }else if(this.last !== false && this.lastActive !== false){
26669                     var last = this.last;
26670                     this.selectRange(this.last,  this.lastActive+1);
26671                     this.grid.getView().focusRow(this.lastActive);
26672                     if(last !== false){
26673                         this.last = last;
26674                     }
26675                 }else{
26676                     this.selectFirstRow();
26677                 }
26678                 this.fireEvent("afterselectionchange", this);
26679             },
26680             scope: this
26681         });
26682         this.grid.store.on('load', function(){
26683             this.selections.clear();
26684         },this);
26685         /*
26686         var view = this.grid.view;
26687         view.on("refresh", this.onRefresh, this);
26688         view.on("rowupdated", this.onRowUpdated, this);
26689         view.on("rowremoved", this.onRemove, this);
26690         */
26691     },
26692
26693     // private
26694     onRefresh : function()
26695     {
26696         var ds = this.grid.store, i, v = this.grid.view;
26697         var s = this.selections;
26698         s.each(function(r){
26699             if((i = ds.indexOfId(r.id)) != -1){
26700                 v.onRowSelect(i);
26701             }else{
26702                 s.remove(r);
26703             }
26704         });
26705     },
26706
26707     // private
26708     onRemove : function(v, index, r){
26709         this.selections.remove(r);
26710     },
26711
26712     // private
26713     onRowUpdated : function(v, index, r){
26714         if(this.isSelected(r)){
26715             v.onRowSelect(index);
26716         }
26717     },
26718
26719     /**
26720      * Select records.
26721      * @param {Array} records The records to select
26722      * @param {Boolean} keepExisting (optional) True to keep existing selections
26723      */
26724     selectRecords : function(records, keepExisting)
26725     {
26726         if(!keepExisting){
26727             this.clearSelections();
26728         }
26729             var ds = this.grid.store;
26730         for(var i = 0, len = records.length; i < len; i++){
26731             this.selectRow(ds.indexOf(records[i]), true);
26732         }
26733     },
26734
26735     /**
26736      * Gets the number of selected rows.
26737      * @return {Number}
26738      */
26739     getCount : function(){
26740         return this.selections.length;
26741     },
26742
26743     /**
26744      * Selects the first row in the grid.
26745      */
26746     selectFirstRow : function(){
26747         this.selectRow(0);
26748     },
26749
26750     /**
26751      * Select the last row.
26752      * @param {Boolean} keepExisting (optional) True to keep existing selections
26753      */
26754     selectLastRow : function(keepExisting){
26755         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26756         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26757     },
26758
26759     /**
26760      * Selects the row immediately following the last selected row.
26761      * @param {Boolean} keepExisting (optional) True to keep existing selections
26762      */
26763     selectNext : function(keepExisting)
26764     {
26765             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26766             this.selectRow(this.last+1, keepExisting);
26767             this.grid.getView().focusRow(this.last);
26768         }
26769     },
26770
26771     /**
26772      * Selects the row that precedes the last selected row.
26773      * @param {Boolean} keepExisting (optional) True to keep existing selections
26774      */
26775     selectPrevious : function(keepExisting){
26776         if(this.last){
26777             this.selectRow(this.last-1, keepExisting);
26778             this.grid.getView().focusRow(this.last);
26779         }
26780     },
26781
26782     /**
26783      * Returns the selected records
26784      * @return {Array} Array of selected records
26785      */
26786     getSelections : function(){
26787         return [].concat(this.selections.items);
26788     },
26789
26790     /**
26791      * Returns the first selected record.
26792      * @return {Record}
26793      */
26794     getSelected : function(){
26795         return this.selections.itemAt(0);
26796     },
26797
26798
26799     /**
26800      * Clears all selections.
26801      */
26802     clearSelections : function(fast)
26803     {
26804         if(this.locked) {
26805             return;
26806         }
26807         if(fast !== true){
26808                 var ds = this.grid.store;
26809             var s = this.selections;
26810             s.each(function(r){
26811                 this.deselectRow(ds.indexOfId(r.id));
26812             }, this);
26813             s.clear();
26814         }else{
26815             this.selections.clear();
26816         }
26817         this.last = false;
26818     },
26819
26820
26821     /**
26822      * Selects all rows.
26823      */
26824     selectAll : function(){
26825         if(this.locked) {
26826             return;
26827         }
26828         this.selections.clear();
26829         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26830             this.selectRow(i, true);
26831         }
26832     },
26833
26834     /**
26835      * Returns True if there is a selection.
26836      * @return {Boolean}
26837      */
26838     hasSelection : function(){
26839         return this.selections.length > 0;
26840     },
26841
26842     /**
26843      * Returns True if the specified row is selected.
26844      * @param {Number/Record} record The record or index of the record to check
26845      * @return {Boolean}
26846      */
26847     isSelected : function(index){
26848             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26849         return (r && this.selections.key(r.id) ? true : false);
26850     },
26851
26852     /**
26853      * Returns True if the specified record id is selected.
26854      * @param {String} id The id of record to check
26855      * @return {Boolean}
26856      */
26857     isIdSelected : function(id){
26858         return (this.selections.key(id) ? true : false);
26859     },
26860
26861
26862     // private
26863     handleMouseDBClick : function(e, t){
26864         
26865     },
26866     // private
26867     handleMouseDown : function(e, t)
26868     {
26869             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26870         if(this.isLocked() || rowIndex < 0 ){
26871             return;
26872         };
26873         if(e.shiftKey && this.last !== false){
26874             var last = this.last;
26875             this.selectRange(last, rowIndex, e.ctrlKey);
26876             this.last = last; // reset the last
26877             t.focus();
26878     
26879         }else{
26880             var isSelected = this.isSelected(rowIndex);
26881             //Roo.log("select row:" + rowIndex);
26882             if(isSelected){
26883                 this.deselectRow(rowIndex);
26884             } else {
26885                         this.selectRow(rowIndex, true);
26886             }
26887     
26888             /*
26889                 if(e.button !== 0 && isSelected){
26890                 alert('rowIndex 2: ' + rowIndex);
26891                     view.focusRow(rowIndex);
26892                 }else if(e.ctrlKey && isSelected){
26893                     this.deselectRow(rowIndex);
26894                 }else if(!isSelected){
26895                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26896                     view.focusRow(rowIndex);
26897                 }
26898             */
26899         }
26900         this.fireEvent("afterselectionchange", this);
26901     },
26902     // private
26903     handleDragableRowClick :  function(grid, rowIndex, e) 
26904     {
26905         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26906             this.selectRow(rowIndex, false);
26907             grid.view.focusRow(rowIndex);
26908              this.fireEvent("afterselectionchange", this);
26909         }
26910     },
26911     
26912     /**
26913      * Selects multiple rows.
26914      * @param {Array} rows Array of the indexes of the row to select
26915      * @param {Boolean} keepExisting (optional) True to keep existing selections
26916      */
26917     selectRows : function(rows, keepExisting){
26918         if(!keepExisting){
26919             this.clearSelections();
26920         }
26921         for(var i = 0, len = rows.length; i < len; i++){
26922             this.selectRow(rows[i], true);
26923         }
26924     },
26925
26926     /**
26927      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26928      * @param {Number} startRow The index of the first row in the range
26929      * @param {Number} endRow The index of the last row in the range
26930      * @param {Boolean} keepExisting (optional) True to retain existing selections
26931      */
26932     selectRange : function(startRow, endRow, keepExisting){
26933         if(this.locked) {
26934             return;
26935         }
26936         if(!keepExisting){
26937             this.clearSelections();
26938         }
26939         if(startRow <= endRow){
26940             for(var i = startRow; i <= endRow; i++){
26941                 this.selectRow(i, true);
26942             }
26943         }else{
26944             for(var i = startRow; i >= endRow; i--){
26945                 this.selectRow(i, true);
26946             }
26947         }
26948     },
26949
26950     /**
26951      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26952      * @param {Number} startRow The index of the first row in the range
26953      * @param {Number} endRow The index of the last row in the range
26954      */
26955     deselectRange : function(startRow, endRow, preventViewNotify){
26956         if(this.locked) {
26957             return;
26958         }
26959         for(var i = startRow; i <= endRow; i++){
26960             this.deselectRow(i, preventViewNotify);
26961         }
26962     },
26963
26964     /**
26965      * Selects a row.
26966      * @param {Number} row The index of the row to select
26967      * @param {Boolean} keepExisting (optional) True to keep existing selections
26968      */
26969     selectRow : function(index, keepExisting, preventViewNotify)
26970     {
26971             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26972             return;
26973         }
26974         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26975             if(!keepExisting || this.singleSelect){
26976                 this.clearSelections();
26977             }
26978             
26979             var r = this.grid.store.getAt(index);
26980             //console.log('selectRow - record id :' + r.id);
26981             
26982             this.selections.add(r);
26983             this.last = this.lastActive = index;
26984             if(!preventViewNotify){
26985                 var proxy = new Roo.Element(
26986                                 this.grid.getRowDom(index)
26987                 );
26988                 proxy.addClass('bg-info info');
26989             }
26990             this.fireEvent("rowselect", this, index, r);
26991             this.fireEvent("selectionchange", this);
26992         }
26993     },
26994
26995     /**
26996      * Deselects a row.
26997      * @param {Number} row The index of the row to deselect
26998      */
26999     deselectRow : function(index, preventViewNotify)
27000     {
27001         if(this.locked) {
27002             return;
27003         }
27004         if(this.last == index){
27005             this.last = false;
27006         }
27007         if(this.lastActive == index){
27008             this.lastActive = false;
27009         }
27010         
27011         var r = this.grid.store.getAt(index);
27012         if (!r) {
27013             return;
27014         }
27015         
27016         this.selections.remove(r);
27017         //.console.log('deselectRow - record id :' + r.id);
27018         if(!preventViewNotify){
27019         
27020             var proxy = new Roo.Element(
27021                 this.grid.getRowDom(index)
27022             );
27023             proxy.removeClass('bg-info info');
27024         }
27025         this.fireEvent("rowdeselect", this, index);
27026         this.fireEvent("selectionchange", this);
27027     },
27028
27029     // private
27030     restoreLast : function(){
27031         if(this._last){
27032             this.last = this._last;
27033         }
27034     },
27035
27036     // private
27037     acceptsNav : function(row, col, cm){
27038         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27039     },
27040
27041     // private
27042     onEditorKey : function(field, e){
27043         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27044         if(k == e.TAB){
27045             e.stopEvent();
27046             ed.completeEdit();
27047             if(e.shiftKey){
27048                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27049             }else{
27050                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27051             }
27052         }else if(k == e.ENTER && !e.ctrlKey){
27053             e.stopEvent();
27054             ed.completeEdit();
27055             if(e.shiftKey){
27056                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27057             }else{
27058                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27059             }
27060         }else if(k == e.ESC){
27061             ed.cancelEdit();
27062         }
27063         if(newCell){
27064             g.startEditing(newCell[0], newCell[1]);
27065         }
27066     }
27067 });
27068 /*
27069  * Based on:
27070  * Ext JS Library 1.1.1
27071  * Copyright(c) 2006-2007, Ext JS, LLC.
27072  *
27073  * Originally Released Under LGPL - original licence link has changed is not relivant.
27074  *
27075  * Fork - LGPL
27076  * <script type="text/javascript">
27077  */
27078  
27079 /**
27080  * @class Roo.bootstrap.PagingToolbar
27081  * @extends Roo.bootstrap.NavSimplebar
27082  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27083  * @constructor
27084  * Create a new PagingToolbar
27085  * @param {Object} config The config object
27086  * @param {Roo.data.Store} store
27087  */
27088 Roo.bootstrap.PagingToolbar = function(config)
27089 {
27090     // old args format still supported... - xtype is prefered..
27091         // created from xtype...
27092     
27093     this.ds = config.dataSource;
27094     
27095     if (config.store && !this.ds) {
27096         this.store= Roo.factory(config.store, Roo.data);
27097         this.ds = this.store;
27098         this.ds.xmodule = this.xmodule || false;
27099     }
27100     
27101     this.toolbarItems = [];
27102     if (config.items) {
27103         this.toolbarItems = config.items;
27104     }
27105     
27106     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27107     
27108     this.cursor = 0;
27109     
27110     if (this.ds) { 
27111         this.bind(this.ds);
27112     }
27113     
27114     if (Roo.bootstrap.version == 4) {
27115         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27116     } else {
27117         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27118     }
27119     
27120 };
27121
27122 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27123     /**
27124      * @cfg {Roo.data.Store} dataSource
27125      * The underlying data store providing the paged data
27126      */
27127     /**
27128      * @cfg {String/HTMLElement/Element} container
27129      * container The id or element that will contain the toolbar
27130      */
27131     /**
27132      * @cfg {Boolean} displayInfo
27133      * True to display the displayMsg (defaults to false)
27134      */
27135     /**
27136      * @cfg {Number} pageSize
27137      * The number of records to display per page (defaults to 20)
27138      */
27139     pageSize: 20,
27140     /**
27141      * @cfg {String} displayMsg
27142      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27143      */
27144     displayMsg : 'Displaying {0} - {1} of {2}',
27145     /**
27146      * @cfg {String} emptyMsg
27147      * The message to display when no records are found (defaults to "No data to display")
27148      */
27149     emptyMsg : 'No data to display',
27150     /**
27151      * Customizable piece of the default paging text (defaults to "Page")
27152      * @type String
27153      */
27154     beforePageText : "Page",
27155     /**
27156      * Customizable piece of the default paging text (defaults to "of %0")
27157      * @type String
27158      */
27159     afterPageText : "of {0}",
27160     /**
27161      * Customizable piece of the default paging text (defaults to "First Page")
27162      * @type String
27163      */
27164     firstText : "First Page",
27165     /**
27166      * Customizable piece of the default paging text (defaults to "Previous Page")
27167      * @type String
27168      */
27169     prevText : "Previous Page",
27170     /**
27171      * Customizable piece of the default paging text (defaults to "Next Page")
27172      * @type String
27173      */
27174     nextText : "Next Page",
27175     /**
27176      * Customizable piece of the default paging text (defaults to "Last Page")
27177      * @type String
27178      */
27179     lastText : "Last Page",
27180     /**
27181      * Customizable piece of the default paging text (defaults to "Refresh")
27182      * @type String
27183      */
27184     refreshText : "Refresh",
27185
27186     buttons : false,
27187     // private
27188     onRender : function(ct, position) 
27189     {
27190         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27191         this.navgroup.parentId = this.id;
27192         this.navgroup.onRender(this.el, null);
27193         // add the buttons to the navgroup
27194         
27195         if(this.displayInfo){
27196             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27197             this.displayEl = this.el.select('.x-paging-info', true).first();
27198 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27199 //            this.displayEl = navel.el.select('span',true).first();
27200         }
27201         
27202         var _this = this;
27203         
27204         if(this.buttons){
27205             Roo.each(_this.buttons, function(e){ // this might need to use render????
27206                Roo.factory(e).render(_this.el);
27207             });
27208         }
27209             
27210         Roo.each(_this.toolbarItems, function(e) {
27211             _this.navgroup.addItem(e);
27212         });
27213         
27214         
27215         this.first = this.navgroup.addItem({
27216             tooltip: this.firstText,
27217             cls: "prev btn-outline-secondary",
27218             html : ' <i class="fa fa-step-backward"></i>',
27219             disabled: true,
27220             preventDefault: true,
27221             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27222         });
27223         
27224         this.prev =  this.navgroup.addItem({
27225             tooltip: this.prevText,
27226             cls: "prev btn-outline-secondary",
27227             html : ' <i class="fa fa-backward"></i>',
27228             disabled: true,
27229             preventDefault: true,
27230             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27231         });
27232     //this.addSeparator();
27233         
27234         
27235         var field = this.navgroup.addItem( {
27236             tagtype : 'span',
27237             cls : 'x-paging-position  btn-outline-secondary',
27238              disabled: true,
27239             html : this.beforePageText  +
27240                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27241                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27242          } ); //?? escaped?
27243         
27244         this.field = field.el.select('input', true).first();
27245         this.field.on("keydown", this.onPagingKeydown, this);
27246         this.field.on("focus", function(){this.dom.select();});
27247     
27248     
27249         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27250         //this.field.setHeight(18);
27251         //this.addSeparator();
27252         this.next = this.navgroup.addItem({
27253             tooltip: this.nextText,
27254             cls: "next btn-outline-secondary",
27255             html : ' <i class="fa fa-forward"></i>',
27256             disabled: true,
27257             preventDefault: true,
27258             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27259         });
27260         this.last = this.navgroup.addItem({
27261             tooltip: this.lastText,
27262             html : ' <i class="fa fa-step-forward"></i>',
27263             cls: "next btn-outline-secondary",
27264             disabled: true,
27265             preventDefault: true,
27266             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27267         });
27268     //this.addSeparator();
27269         this.loading = this.navgroup.addItem({
27270             tooltip: this.refreshText,
27271             cls: "btn-outline-secondary",
27272             html : ' <i class="fa fa-refresh"></i>',
27273             preventDefault: true,
27274             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27275         });
27276         
27277     },
27278
27279     // private
27280     updateInfo : function(){
27281         if(this.displayEl){
27282             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27283             var msg = count == 0 ?
27284                 this.emptyMsg :
27285                 String.format(
27286                     this.displayMsg,
27287                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27288                 );
27289             this.displayEl.update(msg);
27290         }
27291     },
27292
27293     // private
27294     onLoad : function(ds, r, o)
27295     {
27296         this.cursor = o.params.start ? o.params.start : 0;
27297         
27298         var d = this.getPageData(),
27299             ap = d.activePage,
27300             ps = d.pages;
27301         
27302         
27303         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27304         this.field.dom.value = ap;
27305         this.first.setDisabled(ap == 1);
27306         this.prev.setDisabled(ap == 1);
27307         this.next.setDisabled(ap == ps);
27308         this.last.setDisabled(ap == ps);
27309         this.loading.enable();
27310         this.updateInfo();
27311     },
27312
27313     // private
27314     getPageData : function(){
27315         var total = this.ds.getTotalCount();
27316         return {
27317             total : total,
27318             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27319             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27320         };
27321     },
27322
27323     // private
27324     onLoadError : function(){
27325         this.loading.enable();
27326     },
27327
27328     // private
27329     onPagingKeydown : function(e){
27330         var k = e.getKey();
27331         var d = this.getPageData();
27332         if(k == e.RETURN){
27333             var v = this.field.dom.value, pageNum;
27334             if(!v || isNaN(pageNum = parseInt(v, 10))){
27335                 this.field.dom.value = d.activePage;
27336                 return;
27337             }
27338             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27340             e.stopEvent();
27341         }
27342         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))
27343         {
27344           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27345           this.field.dom.value = pageNum;
27346           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27347           e.stopEvent();
27348         }
27349         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27350         {
27351           var v = this.field.dom.value, pageNum; 
27352           var increment = (e.shiftKey) ? 10 : 1;
27353           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27354                 increment *= -1;
27355           }
27356           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27357             this.field.dom.value = d.activePage;
27358             return;
27359           }
27360           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27361           {
27362             this.field.dom.value = parseInt(v, 10) + increment;
27363             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27364             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27365           }
27366           e.stopEvent();
27367         }
27368     },
27369
27370     // private
27371     beforeLoad : function(){
27372         if(this.loading){
27373             this.loading.disable();
27374         }
27375     },
27376
27377     // private
27378     onClick : function(which){
27379         
27380         var ds = this.ds;
27381         if (!ds) {
27382             return;
27383         }
27384         
27385         switch(which){
27386             case "first":
27387                 ds.load({params:{start: 0, limit: this.pageSize}});
27388             break;
27389             case "prev":
27390                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27391             break;
27392             case "next":
27393                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27394             break;
27395             case "last":
27396                 var total = ds.getTotalCount();
27397                 var extra = total % this.pageSize;
27398                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27399                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27400             break;
27401             case "refresh":
27402                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27403             break;
27404         }
27405     },
27406
27407     /**
27408      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27409      * @param {Roo.data.Store} store The data store to unbind
27410      */
27411     unbind : function(ds){
27412         ds.un("beforeload", this.beforeLoad, this);
27413         ds.un("load", this.onLoad, this);
27414         ds.un("loadexception", this.onLoadError, this);
27415         ds.un("remove", this.updateInfo, this);
27416         ds.un("add", this.updateInfo, this);
27417         this.ds = undefined;
27418     },
27419
27420     /**
27421      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27422      * @param {Roo.data.Store} store The data store to bind
27423      */
27424     bind : function(ds){
27425         ds.on("beforeload", this.beforeLoad, this);
27426         ds.on("load", this.onLoad, this);
27427         ds.on("loadexception", this.onLoadError, this);
27428         ds.on("remove", this.updateInfo, this);
27429         ds.on("add", this.updateInfo, this);
27430         this.ds = ds;
27431     }
27432 });/*
27433  * - LGPL
27434  *
27435  * element
27436  * 
27437  */
27438
27439 /**
27440  * @class Roo.bootstrap.MessageBar
27441  * @extends Roo.bootstrap.Component
27442  * Bootstrap MessageBar class
27443  * @cfg {String} html contents of the MessageBar
27444  * @cfg {String} weight (info | success | warning | danger) default info
27445  * @cfg {String} beforeClass insert the bar before the given class
27446  * @cfg {Boolean} closable (true | false) default false
27447  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27448  * 
27449  * @constructor
27450  * Create a new Element
27451  * @param {Object} config The config object
27452  */
27453
27454 Roo.bootstrap.MessageBar = function(config){
27455     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27456 };
27457
27458 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27459     
27460     html: '',
27461     weight: 'info',
27462     closable: false,
27463     fixed: false,
27464     beforeClass: 'bootstrap-sticky-wrap',
27465     
27466     getAutoCreate : function(){
27467         
27468         var cfg = {
27469             tag: 'div',
27470             cls: 'alert alert-dismissable alert-' + this.weight,
27471             cn: [
27472                 {
27473                     tag: 'span',
27474                     cls: 'message',
27475                     html: this.html || ''
27476                 }
27477             ]
27478         };
27479         
27480         if(this.fixed){
27481             cfg.cls += ' alert-messages-fixed';
27482         }
27483         
27484         if(this.closable){
27485             cfg.cn.push({
27486                 tag: 'button',
27487                 cls: 'close',
27488                 html: 'x'
27489             });
27490         }
27491         
27492         return cfg;
27493     },
27494     
27495     onRender : function(ct, position)
27496     {
27497         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27498         
27499         if(!this.el){
27500             var cfg = Roo.apply({},  this.getAutoCreate());
27501             cfg.id = Roo.id();
27502             
27503             if (this.cls) {
27504                 cfg.cls += ' ' + this.cls;
27505             }
27506             if (this.style) {
27507                 cfg.style = this.style;
27508             }
27509             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27510             
27511             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27512         }
27513         
27514         this.el.select('>button.close').on('click', this.hide, this);
27515         
27516     },
27517     
27518     show : function()
27519     {
27520         if (!this.rendered) {
27521             this.render();
27522         }
27523         
27524         this.el.show();
27525         
27526         this.fireEvent('show', this);
27527         
27528     },
27529     
27530     hide : function()
27531     {
27532         if (!this.rendered) {
27533             this.render();
27534         }
27535         
27536         this.el.hide();
27537         
27538         this.fireEvent('hide', this);
27539     },
27540     
27541     update : function()
27542     {
27543 //        var e = this.el.dom.firstChild;
27544 //        
27545 //        if(this.closable){
27546 //            e = e.nextSibling;
27547 //        }
27548 //        
27549 //        e.data = this.html || '';
27550
27551         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27552     }
27553    
27554 });
27555
27556  
27557
27558      /*
27559  * - LGPL
27560  *
27561  * Graph
27562  * 
27563  */
27564
27565
27566 /**
27567  * @class Roo.bootstrap.Graph
27568  * @extends Roo.bootstrap.Component
27569  * Bootstrap Graph class
27570 > Prameters
27571  -sm {number} sm 4
27572  -md {number} md 5
27573  @cfg {String} graphtype  bar | vbar | pie
27574  @cfg {number} g_x coodinator | centre x (pie)
27575  @cfg {number} g_y coodinator | centre y (pie)
27576  @cfg {number} g_r radius (pie)
27577  @cfg {number} g_height height of the chart (respected by all elements in the set)
27578  @cfg {number} g_width width of the chart (respected by all elements in the set)
27579  @cfg {Object} title The title of the chart
27580     
27581  -{Array}  values
27582  -opts (object) options for the chart 
27583      o {
27584      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27585      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27586      o vgutter (number)
27587      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.
27588      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27589      o to
27590      o stretch (boolean)
27591      o }
27592  -opts (object) options for the pie
27593      o{
27594      o cut
27595      o startAngle (number)
27596      o endAngle (number)
27597      } 
27598  *
27599  * @constructor
27600  * Create a new Input
27601  * @param {Object} config The config object
27602  */
27603
27604 Roo.bootstrap.Graph = function(config){
27605     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27606     
27607     this.addEvents({
27608         // img events
27609         /**
27610          * @event click
27611          * The img click event for the img.
27612          * @param {Roo.EventObject} e
27613          */
27614         "click" : true
27615     });
27616 };
27617
27618 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27619     
27620     sm: 4,
27621     md: 5,
27622     graphtype: 'bar',
27623     g_height: 250,
27624     g_width: 400,
27625     g_x: 50,
27626     g_y: 50,
27627     g_r: 30,
27628     opts:{
27629         //g_colors: this.colors,
27630         g_type: 'soft',
27631         g_gutter: '20%'
27632
27633     },
27634     title : false,
27635
27636     getAutoCreate : function(){
27637         
27638         var cfg = {
27639             tag: 'div',
27640             html : null
27641         };
27642         
27643         
27644         return  cfg;
27645     },
27646
27647     onRender : function(ct,position){
27648         
27649         
27650         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27651         
27652         if (typeof(Raphael) == 'undefined') {
27653             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27654             return;
27655         }
27656         
27657         this.raphael = Raphael(this.el.dom);
27658         
27659                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27660                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27661                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27662                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27663                 /*
27664                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27665                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27666                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27667                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27668                 
27669                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27670                 r.barchart(330, 10, 300, 220, data1);
27671                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27672                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27673                 */
27674                 
27675                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27676                 // r.barchart(30, 30, 560, 250,  xdata, {
27677                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27678                 //     axis : "0 0 1 1",
27679                 //     axisxlabels :  xdata
27680                 //     //yvalues : cols,
27681                    
27682                 // });
27683 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27684 //        
27685 //        this.load(null,xdata,{
27686 //                axis : "0 0 1 1",
27687 //                axisxlabels :  xdata
27688 //                });
27689
27690     },
27691
27692     load : function(graphtype,xdata,opts)
27693     {
27694         this.raphael.clear();
27695         if(!graphtype) {
27696             graphtype = this.graphtype;
27697         }
27698         if(!opts){
27699             opts = this.opts;
27700         }
27701         var r = this.raphael,
27702             fin = function () {
27703                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27704             },
27705             fout = function () {
27706                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27707             },
27708             pfin = function() {
27709                 this.sector.stop();
27710                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27711
27712                 if (this.label) {
27713                     this.label[0].stop();
27714                     this.label[0].attr({ r: 7.5 });
27715                     this.label[1].attr({ "font-weight": 800 });
27716                 }
27717             },
27718             pfout = function() {
27719                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27720
27721                 if (this.label) {
27722                     this.label[0].animate({ r: 5 }, 500, "bounce");
27723                     this.label[1].attr({ "font-weight": 400 });
27724                 }
27725             };
27726
27727         switch(graphtype){
27728             case 'bar':
27729                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27730                 break;
27731             case 'hbar':
27732                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27733                 break;
27734             case 'pie':
27735 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27736 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27737 //            
27738                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27739                 
27740                 break;
27741
27742         }
27743         
27744         if(this.title){
27745             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27746         }
27747         
27748     },
27749     
27750     setTitle: function(o)
27751     {
27752         this.title = o;
27753     },
27754     
27755     initEvents: function() {
27756         
27757         if(!this.href){
27758             this.el.on('click', this.onClick, this);
27759         }
27760     },
27761     
27762     onClick : function(e)
27763     {
27764         Roo.log('img onclick');
27765         this.fireEvent('click', this, e);
27766     }
27767    
27768 });
27769
27770  
27771 /*
27772  * - LGPL
27773  *
27774  * numberBox
27775  * 
27776  */
27777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27778
27779 /**
27780  * @class Roo.bootstrap.dash.NumberBox
27781  * @extends Roo.bootstrap.Component
27782  * Bootstrap NumberBox class
27783  * @cfg {String} headline Box headline
27784  * @cfg {String} content Box content
27785  * @cfg {String} icon Box icon
27786  * @cfg {String} footer Footer text
27787  * @cfg {String} fhref Footer href
27788  * 
27789  * @constructor
27790  * Create a new NumberBox
27791  * @param {Object} config The config object
27792  */
27793
27794
27795 Roo.bootstrap.dash.NumberBox = function(config){
27796     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27797     
27798 };
27799
27800 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27801     
27802     headline : '',
27803     content : '',
27804     icon : '',
27805     footer : '',
27806     fhref : '',
27807     ficon : '',
27808     
27809     getAutoCreate : function(){
27810         
27811         var cfg = {
27812             tag : 'div',
27813             cls : 'small-box ',
27814             cn : [
27815                 {
27816                     tag : 'div',
27817                     cls : 'inner',
27818                     cn :[
27819                         {
27820                             tag : 'h3',
27821                             cls : 'roo-headline',
27822                             html : this.headline
27823                         },
27824                         {
27825                             tag : 'p',
27826                             cls : 'roo-content',
27827                             html : this.content
27828                         }
27829                     ]
27830                 }
27831             ]
27832         };
27833         
27834         if(this.icon){
27835             cfg.cn.push({
27836                 tag : 'div',
27837                 cls : 'icon',
27838                 cn :[
27839                     {
27840                         tag : 'i',
27841                         cls : 'ion ' + this.icon
27842                     }
27843                 ]
27844             });
27845         }
27846         
27847         if(this.footer){
27848             var footer = {
27849                 tag : 'a',
27850                 cls : 'small-box-footer',
27851                 href : this.fhref || '#',
27852                 html : this.footer
27853             };
27854             
27855             cfg.cn.push(footer);
27856             
27857         }
27858         
27859         return  cfg;
27860     },
27861
27862     onRender : function(ct,position){
27863         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27864
27865
27866        
27867                 
27868     },
27869
27870     setHeadline: function (value)
27871     {
27872         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27873     },
27874     
27875     setFooter: function (value, href)
27876     {
27877         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27878         
27879         if(href){
27880             this.el.select('a.small-box-footer',true).first().attr('href', href);
27881         }
27882         
27883     },
27884
27885     setContent: function (value)
27886     {
27887         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27888     },
27889
27890     initEvents: function() 
27891     {   
27892         
27893     }
27894     
27895 });
27896
27897  
27898 /*
27899  * - LGPL
27900  *
27901  * TabBox
27902  * 
27903  */
27904 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27905
27906 /**
27907  * @class Roo.bootstrap.dash.TabBox
27908  * @extends Roo.bootstrap.Component
27909  * Bootstrap TabBox class
27910  * @cfg {String} title Title of the TabBox
27911  * @cfg {String} icon Icon of the TabBox
27912  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27913  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27914  * 
27915  * @constructor
27916  * Create a new TabBox
27917  * @param {Object} config The config object
27918  */
27919
27920
27921 Roo.bootstrap.dash.TabBox = function(config){
27922     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27923     this.addEvents({
27924         // raw events
27925         /**
27926          * @event addpane
27927          * When a pane is added
27928          * @param {Roo.bootstrap.dash.TabPane} pane
27929          */
27930         "addpane" : true,
27931         /**
27932          * @event activatepane
27933          * When a pane is activated
27934          * @param {Roo.bootstrap.dash.TabPane} pane
27935          */
27936         "activatepane" : true
27937         
27938          
27939     });
27940     
27941     this.panes = [];
27942 };
27943
27944 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27945
27946     title : '',
27947     icon : false,
27948     showtabs : true,
27949     tabScrollable : false,
27950     
27951     getChildContainer : function()
27952     {
27953         return this.el.select('.tab-content', true).first();
27954     },
27955     
27956     getAutoCreate : function(){
27957         
27958         var header = {
27959             tag: 'li',
27960             cls: 'pull-left header',
27961             html: this.title,
27962             cn : []
27963         };
27964         
27965         if(this.icon){
27966             header.cn.push({
27967                 tag: 'i',
27968                 cls: 'fa ' + this.icon
27969             });
27970         }
27971         
27972         var h = {
27973             tag: 'ul',
27974             cls: 'nav nav-tabs pull-right',
27975             cn: [
27976                 header
27977             ]
27978         };
27979         
27980         if(this.tabScrollable){
27981             h = {
27982                 tag: 'div',
27983                 cls: 'tab-header',
27984                 cn: [
27985                     {
27986                         tag: 'ul',
27987                         cls: 'nav nav-tabs pull-right',
27988                         cn: [
27989                             header
27990                         ]
27991                     }
27992                 ]
27993             };
27994         }
27995         
27996         var cfg = {
27997             tag: 'div',
27998             cls: 'nav-tabs-custom',
27999             cn: [
28000                 h,
28001                 {
28002                     tag: 'div',
28003                     cls: 'tab-content no-padding',
28004                     cn: []
28005                 }
28006             ]
28007         };
28008
28009         return  cfg;
28010     },
28011     initEvents : function()
28012     {
28013         //Roo.log('add add pane handler');
28014         this.on('addpane', this.onAddPane, this);
28015     },
28016      /**
28017      * Updates the box title
28018      * @param {String} html to set the title to.
28019      */
28020     setTitle : function(value)
28021     {
28022         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28023     },
28024     onAddPane : function(pane)
28025     {
28026         this.panes.push(pane);
28027         //Roo.log('addpane');
28028         //Roo.log(pane);
28029         // tabs are rendere left to right..
28030         if(!this.showtabs){
28031             return;
28032         }
28033         
28034         var ctr = this.el.select('.nav-tabs', true).first();
28035          
28036          
28037         var existing = ctr.select('.nav-tab',true);
28038         var qty = existing.getCount();;
28039         
28040         
28041         var tab = ctr.createChild({
28042             tag : 'li',
28043             cls : 'nav-tab' + (qty ? '' : ' active'),
28044             cn : [
28045                 {
28046                     tag : 'a',
28047                     href:'#',
28048                     html : pane.title
28049                 }
28050             ]
28051         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28052         pane.tab = tab;
28053         
28054         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28055         if (!qty) {
28056             pane.el.addClass('active');
28057         }
28058         
28059                 
28060     },
28061     onTabClick : function(ev,un,ob,pane)
28062     {
28063         //Roo.log('tab - prev default');
28064         ev.preventDefault();
28065         
28066         
28067         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28068         pane.tab.addClass('active');
28069         //Roo.log(pane.title);
28070         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28071         // technically we should have a deactivate event.. but maybe add later.
28072         // and it should not de-activate the selected tab...
28073         this.fireEvent('activatepane', pane);
28074         pane.el.addClass('active');
28075         pane.fireEvent('activate');
28076         
28077         
28078     },
28079     
28080     getActivePane : function()
28081     {
28082         var r = false;
28083         Roo.each(this.panes, function(p) {
28084             if(p.el.hasClass('active')){
28085                 r = p;
28086                 return false;
28087             }
28088             
28089             return;
28090         });
28091         
28092         return r;
28093     }
28094     
28095     
28096 });
28097
28098  
28099 /*
28100  * - LGPL
28101  *
28102  * Tab pane
28103  * 
28104  */
28105 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28106 /**
28107  * @class Roo.bootstrap.TabPane
28108  * @extends Roo.bootstrap.Component
28109  * Bootstrap TabPane class
28110  * @cfg {Boolean} active (false | true) Default false
28111  * @cfg {String} title title of panel
28112
28113  * 
28114  * @constructor
28115  * Create a new TabPane
28116  * @param {Object} config The config object
28117  */
28118
28119 Roo.bootstrap.dash.TabPane = function(config){
28120     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28121     
28122     this.addEvents({
28123         // raw events
28124         /**
28125          * @event activate
28126          * When a pane is activated
28127          * @param {Roo.bootstrap.dash.TabPane} pane
28128          */
28129         "activate" : true
28130          
28131     });
28132 };
28133
28134 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28135     
28136     active : false,
28137     title : '',
28138     
28139     // the tabBox that this is attached to.
28140     tab : false,
28141      
28142     getAutoCreate : function() 
28143     {
28144         var cfg = {
28145             tag: 'div',
28146             cls: 'tab-pane'
28147         };
28148         
28149         if(this.active){
28150             cfg.cls += ' active';
28151         }
28152         
28153         return cfg;
28154     },
28155     initEvents  : function()
28156     {
28157         //Roo.log('trigger add pane handler');
28158         this.parent().fireEvent('addpane', this)
28159     },
28160     
28161      /**
28162      * Updates the tab title 
28163      * @param {String} html to set the title to.
28164      */
28165     setTitle: function(str)
28166     {
28167         if (!this.tab) {
28168             return;
28169         }
28170         this.title = str;
28171         this.tab.select('a', true).first().dom.innerHTML = str;
28172         
28173     }
28174     
28175     
28176     
28177 });
28178
28179  
28180
28181
28182  /*
28183  * - LGPL
28184  *
28185  * menu
28186  * 
28187  */
28188 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28189
28190 /**
28191  * @class Roo.bootstrap.menu.Menu
28192  * @extends Roo.bootstrap.Component
28193  * Bootstrap Menu class - container for Menu
28194  * @cfg {String} html Text of the menu
28195  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28196  * @cfg {String} icon Font awesome icon
28197  * @cfg {String} pos Menu align to (top | bottom) default bottom
28198  * 
28199  * 
28200  * @constructor
28201  * Create a new Menu
28202  * @param {Object} config The config object
28203  */
28204
28205
28206 Roo.bootstrap.menu.Menu = function(config){
28207     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28208     
28209     this.addEvents({
28210         /**
28211          * @event beforeshow
28212          * Fires before this menu is displayed
28213          * @param {Roo.bootstrap.menu.Menu} this
28214          */
28215         beforeshow : true,
28216         /**
28217          * @event beforehide
28218          * Fires before this menu is hidden
28219          * @param {Roo.bootstrap.menu.Menu} this
28220          */
28221         beforehide : true,
28222         /**
28223          * @event show
28224          * Fires after this menu is displayed
28225          * @param {Roo.bootstrap.menu.Menu} this
28226          */
28227         show : true,
28228         /**
28229          * @event hide
28230          * Fires after this menu is hidden
28231          * @param {Roo.bootstrap.menu.Menu} this
28232          */
28233         hide : true,
28234         /**
28235          * @event click
28236          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28237          * @param {Roo.bootstrap.menu.Menu} this
28238          * @param {Roo.EventObject} e
28239          */
28240         click : true
28241     });
28242     
28243 };
28244
28245 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28246     
28247     submenu : false,
28248     html : '',
28249     weight : 'default',
28250     icon : false,
28251     pos : 'bottom',
28252     
28253     
28254     getChildContainer : function() {
28255         if(this.isSubMenu){
28256             return this.el;
28257         }
28258         
28259         return this.el.select('ul.dropdown-menu', true).first();  
28260     },
28261     
28262     getAutoCreate : function()
28263     {
28264         var text = [
28265             {
28266                 tag : 'span',
28267                 cls : 'roo-menu-text',
28268                 html : this.html
28269             }
28270         ];
28271         
28272         if(this.icon){
28273             text.unshift({
28274                 tag : 'i',
28275                 cls : 'fa ' + this.icon
28276             })
28277         }
28278         
28279         
28280         var cfg = {
28281             tag : 'div',
28282             cls : 'btn-group',
28283             cn : [
28284                 {
28285                     tag : 'button',
28286                     cls : 'dropdown-button btn btn-' + this.weight,
28287                     cn : text
28288                 },
28289                 {
28290                     tag : 'button',
28291                     cls : 'dropdown-toggle btn btn-' + this.weight,
28292                     cn : [
28293                         {
28294                             tag : 'span',
28295                             cls : 'caret'
28296                         }
28297                     ]
28298                 },
28299                 {
28300                     tag : 'ul',
28301                     cls : 'dropdown-menu'
28302                 }
28303             ]
28304             
28305         };
28306         
28307         if(this.pos == 'top'){
28308             cfg.cls += ' dropup';
28309         }
28310         
28311         if(this.isSubMenu){
28312             cfg = {
28313                 tag : 'ul',
28314                 cls : 'dropdown-menu'
28315             }
28316         }
28317         
28318         return cfg;
28319     },
28320     
28321     onRender : function(ct, position)
28322     {
28323         this.isSubMenu = ct.hasClass('dropdown-submenu');
28324         
28325         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28326     },
28327     
28328     initEvents : function() 
28329     {
28330         if(this.isSubMenu){
28331             return;
28332         }
28333         
28334         this.hidden = true;
28335         
28336         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28337         this.triggerEl.on('click', this.onTriggerPress, this);
28338         
28339         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28340         this.buttonEl.on('click', this.onClick, this);
28341         
28342     },
28343     
28344     list : function()
28345     {
28346         if(this.isSubMenu){
28347             return this.el;
28348         }
28349         
28350         return this.el.select('ul.dropdown-menu', true).first();
28351     },
28352     
28353     onClick : function(e)
28354     {
28355         this.fireEvent("click", this, e);
28356     },
28357     
28358     onTriggerPress  : function(e)
28359     {   
28360         if (this.isVisible()) {
28361             this.hide();
28362         } else {
28363             this.show();
28364         }
28365     },
28366     
28367     isVisible : function(){
28368         return !this.hidden;
28369     },
28370     
28371     show : function()
28372     {
28373         this.fireEvent("beforeshow", this);
28374         
28375         this.hidden = false;
28376         this.el.addClass('open');
28377         
28378         Roo.get(document).on("mouseup", this.onMouseUp, this);
28379         
28380         this.fireEvent("show", this);
28381         
28382         
28383     },
28384     
28385     hide : function()
28386     {
28387         this.fireEvent("beforehide", this);
28388         
28389         this.hidden = true;
28390         this.el.removeClass('open');
28391         
28392         Roo.get(document).un("mouseup", this.onMouseUp);
28393         
28394         this.fireEvent("hide", this);
28395     },
28396     
28397     onMouseUp : function()
28398     {
28399         this.hide();
28400     }
28401     
28402 });
28403
28404  
28405  /*
28406  * - LGPL
28407  *
28408  * menu item
28409  * 
28410  */
28411 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28412
28413 /**
28414  * @class Roo.bootstrap.menu.Item
28415  * @extends Roo.bootstrap.Component
28416  * Bootstrap MenuItem class
28417  * @cfg {Boolean} submenu (true | false) default false
28418  * @cfg {String} html text of the item
28419  * @cfg {String} href the link
28420  * @cfg {Boolean} disable (true | false) default false
28421  * @cfg {Boolean} preventDefault (true | false) default true
28422  * @cfg {String} icon Font awesome icon
28423  * @cfg {String} pos Submenu align to (left | right) default right 
28424  * 
28425  * 
28426  * @constructor
28427  * Create a new Item
28428  * @param {Object} config The config object
28429  */
28430
28431
28432 Roo.bootstrap.menu.Item = function(config){
28433     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28434     this.addEvents({
28435         /**
28436          * @event mouseover
28437          * Fires when the mouse is hovering over this menu
28438          * @param {Roo.bootstrap.menu.Item} this
28439          * @param {Roo.EventObject} e
28440          */
28441         mouseover : true,
28442         /**
28443          * @event mouseout
28444          * Fires when the mouse exits this menu
28445          * @param {Roo.bootstrap.menu.Item} this
28446          * @param {Roo.EventObject} e
28447          */
28448         mouseout : true,
28449         // raw events
28450         /**
28451          * @event click
28452          * The raw click event for the entire grid.
28453          * @param {Roo.EventObject} e
28454          */
28455         click : true
28456     });
28457 };
28458
28459 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28460     
28461     submenu : false,
28462     href : '',
28463     html : '',
28464     preventDefault: true,
28465     disable : false,
28466     icon : false,
28467     pos : 'right',
28468     
28469     getAutoCreate : function()
28470     {
28471         var text = [
28472             {
28473                 tag : 'span',
28474                 cls : 'roo-menu-item-text',
28475                 html : this.html
28476             }
28477         ];
28478         
28479         if(this.icon){
28480             text.unshift({
28481                 tag : 'i',
28482                 cls : 'fa ' + this.icon
28483             })
28484         }
28485         
28486         var cfg = {
28487             tag : 'li',
28488             cn : [
28489                 {
28490                     tag : 'a',
28491                     href : this.href || '#',
28492                     cn : text
28493                 }
28494             ]
28495         };
28496         
28497         if(this.disable){
28498             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28499         }
28500         
28501         if(this.submenu){
28502             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28503             
28504             if(this.pos == 'left'){
28505                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28506             }
28507         }
28508         
28509         return cfg;
28510     },
28511     
28512     initEvents : function() 
28513     {
28514         this.el.on('mouseover', this.onMouseOver, this);
28515         this.el.on('mouseout', this.onMouseOut, this);
28516         
28517         this.el.select('a', true).first().on('click', this.onClick, this);
28518         
28519     },
28520     
28521     onClick : function(e)
28522     {
28523         if(this.preventDefault){
28524             e.preventDefault();
28525         }
28526         
28527         this.fireEvent("click", this, e);
28528     },
28529     
28530     onMouseOver : function(e)
28531     {
28532         if(this.submenu && this.pos == 'left'){
28533             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28534         }
28535         
28536         this.fireEvent("mouseover", this, e);
28537     },
28538     
28539     onMouseOut : function(e)
28540     {
28541         this.fireEvent("mouseout", this, e);
28542     }
28543 });
28544
28545  
28546
28547  /*
28548  * - LGPL
28549  *
28550  * menu separator
28551  * 
28552  */
28553 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28554
28555 /**
28556  * @class Roo.bootstrap.menu.Separator
28557  * @extends Roo.bootstrap.Component
28558  * Bootstrap Separator class
28559  * 
28560  * @constructor
28561  * Create a new Separator
28562  * @param {Object} config The config object
28563  */
28564
28565
28566 Roo.bootstrap.menu.Separator = function(config){
28567     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28568 };
28569
28570 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28571     
28572     getAutoCreate : function(){
28573         var cfg = {
28574             tag : 'li',
28575             cls: 'divider'
28576         };
28577         
28578         return cfg;
28579     }
28580    
28581 });
28582
28583  
28584
28585  /*
28586  * - LGPL
28587  *
28588  * Tooltip
28589  * 
28590  */
28591
28592 /**
28593  * @class Roo.bootstrap.Tooltip
28594  * Bootstrap Tooltip class
28595  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28596  * to determine which dom element triggers the tooltip.
28597  * 
28598  * It needs to add support for additional attributes like tooltip-position
28599  * 
28600  * @constructor
28601  * Create a new Toolti
28602  * @param {Object} config The config object
28603  */
28604
28605 Roo.bootstrap.Tooltip = function(config){
28606     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28607     
28608     this.alignment = Roo.bootstrap.Tooltip.alignment;
28609     
28610     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28611         this.alignment = config.alignment;
28612     }
28613     
28614 };
28615
28616 Roo.apply(Roo.bootstrap.Tooltip, {
28617     /**
28618      * @function init initialize tooltip monitoring.
28619      * @static
28620      */
28621     currentEl : false,
28622     currentTip : false,
28623     currentRegion : false,
28624     
28625     //  init : delay?
28626     
28627     init : function()
28628     {
28629         Roo.get(document).on('mouseover', this.enter ,this);
28630         Roo.get(document).on('mouseout', this.leave, this);
28631          
28632         
28633         this.currentTip = new Roo.bootstrap.Tooltip();
28634     },
28635     
28636     enter : function(ev)
28637     {
28638         var dom = ev.getTarget();
28639         
28640         //Roo.log(['enter',dom]);
28641         var el = Roo.fly(dom);
28642         if (this.currentEl) {
28643             //Roo.log(dom);
28644             //Roo.log(this.currentEl);
28645             //Roo.log(this.currentEl.contains(dom));
28646             if (this.currentEl == el) {
28647                 return;
28648             }
28649             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28650                 return;
28651             }
28652
28653         }
28654         
28655         if (this.currentTip.el) {
28656             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28657         }    
28658         //Roo.log(ev);
28659         
28660         if(!el || el.dom == document){
28661             return;
28662         }
28663         
28664         var bindEl = el;
28665         
28666         // you can not look for children, as if el is the body.. then everythign is the child..
28667         if (!el.attr('tooltip')) { //
28668             if (!el.select("[tooltip]").elements.length) {
28669                 return;
28670             }
28671             // is the mouse over this child...?
28672             bindEl = el.select("[tooltip]").first();
28673             var xy = ev.getXY();
28674             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28675                 //Roo.log("not in region.");
28676                 return;
28677             }
28678             //Roo.log("child element over..");
28679             
28680         }
28681         this.currentEl = bindEl;
28682         this.currentTip.bind(bindEl);
28683         this.currentRegion = Roo.lib.Region.getRegion(dom);
28684         this.currentTip.enter();
28685         
28686     },
28687     leave : function(ev)
28688     {
28689         var dom = ev.getTarget();
28690         //Roo.log(['leave',dom]);
28691         if (!this.currentEl) {
28692             return;
28693         }
28694         
28695         
28696         if (dom != this.currentEl.dom) {
28697             return;
28698         }
28699         var xy = ev.getXY();
28700         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28701             return;
28702         }
28703         // only activate leave if mouse cursor is outside... bounding box..
28704         
28705         
28706         
28707         
28708         if (this.currentTip) {
28709             this.currentTip.leave();
28710         }
28711         //Roo.log('clear currentEl');
28712         this.currentEl = false;
28713         
28714         
28715     },
28716     alignment : {
28717         'left' : ['r-l', [-2,0], 'right'],
28718         'right' : ['l-r', [2,0], 'left'],
28719         'bottom' : ['t-b', [0,2], 'top'],
28720         'top' : [ 'b-t', [0,-2], 'bottom']
28721     }
28722     
28723 });
28724
28725
28726 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28727     
28728     
28729     bindEl : false,
28730     
28731     delay : null, // can be { show : 300 , hide: 500}
28732     
28733     timeout : null,
28734     
28735     hoverState : null, //???
28736     
28737     placement : 'bottom', 
28738     
28739     alignment : false,
28740     
28741     getAutoCreate : function(){
28742     
28743         var cfg = {
28744            cls : 'tooltip',   
28745            role : 'tooltip',
28746            cn : [
28747                 {
28748                     cls : 'tooltip-arrow arrow'
28749                 },
28750                 {
28751                     cls : 'tooltip-inner'
28752                 }
28753            ]
28754         };
28755         
28756         return cfg;
28757     },
28758     bind : function(el)
28759     {
28760         this.bindEl = el;
28761     },
28762     
28763     initEvents : function()
28764     {
28765         this.arrowEl = this.el.select('.arrow', true).first();
28766         this.innerEl = this.el.select('.tooltip-inner', true).first();
28767     },
28768     
28769     enter : function () {
28770        
28771         if (this.timeout != null) {
28772             clearTimeout(this.timeout);
28773         }
28774         
28775         this.hoverState = 'in';
28776          //Roo.log("enter - show");
28777         if (!this.delay || !this.delay.show) {
28778             this.show();
28779             return;
28780         }
28781         var _t = this;
28782         this.timeout = setTimeout(function () {
28783             if (_t.hoverState == 'in') {
28784                 _t.show();
28785             }
28786         }, this.delay.show);
28787     },
28788     leave : function()
28789     {
28790         clearTimeout(this.timeout);
28791     
28792         this.hoverState = 'out';
28793          if (!this.delay || !this.delay.hide) {
28794             this.hide();
28795             return;
28796         }
28797        
28798         var _t = this;
28799         this.timeout = setTimeout(function () {
28800             //Roo.log("leave - timeout");
28801             
28802             if (_t.hoverState == 'out') {
28803                 _t.hide();
28804                 Roo.bootstrap.Tooltip.currentEl = false;
28805             }
28806         }, delay);
28807     },
28808     
28809     show : function (msg)
28810     {
28811         if (!this.el) {
28812             this.render(document.body);
28813         }
28814         // set content.
28815         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28816         
28817         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28818         
28819         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28820         
28821         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28822                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28823         
28824         var placement = typeof this.placement == 'function' ?
28825             this.placement.call(this, this.el, on_el) :
28826             this.placement;
28827             
28828         var autoToken = /\s?auto?\s?/i;
28829         var autoPlace = autoToken.test(placement);
28830         if (autoPlace) {
28831             placement = placement.replace(autoToken, '') || 'top';
28832         }
28833         
28834         //this.el.detach()
28835         //this.el.setXY([0,0]);
28836         this.el.show();
28837         //this.el.dom.style.display='block';
28838         
28839         //this.el.appendTo(on_el);
28840         
28841         var p = this.getPosition();
28842         var box = this.el.getBox();
28843         
28844         if (autoPlace) {
28845             // fixme..
28846         }
28847         
28848         var align = this.alignment[placement];
28849         
28850         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28851         
28852         if(placement == 'top' || placement == 'bottom'){
28853             if(xy[0] < 0){
28854                 placement = 'right';
28855             }
28856             
28857             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28858                 placement = 'left';
28859             }
28860             
28861             var scroll = Roo.select('body', true).first().getScroll();
28862             
28863             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28864                 placement = 'top';
28865             }
28866             
28867             align = this.alignment[placement];
28868             
28869             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28870             
28871         }
28872         
28873         this.el.alignTo(this.bindEl, align[0],align[1]);
28874         //var arrow = this.el.select('.arrow',true).first();
28875         //arrow.set(align[2], 
28876         
28877         this.el.addClass(placement);
28878         this.el.addClass("bs-tooltip-"+ placement);
28879         
28880         this.el.addClass('in fade show');
28881         
28882         this.hoverState = null;
28883         
28884         if (this.el.hasClass('fade')) {
28885             // fade it?
28886         }
28887         
28888         
28889         
28890         
28891         
28892     },
28893     hide : function()
28894     {
28895          
28896         if (!this.el) {
28897             return;
28898         }
28899         //this.el.setXY([0,0]);
28900         this.el.removeClass(['show', 'in']);
28901         //this.el.hide();
28902         
28903     }
28904     
28905 });
28906  
28907
28908  /*
28909  * - LGPL
28910  *
28911  * Location Picker
28912  * 
28913  */
28914
28915 /**
28916  * @class Roo.bootstrap.LocationPicker
28917  * @extends Roo.bootstrap.Component
28918  * Bootstrap LocationPicker class
28919  * @cfg {Number} latitude Position when init default 0
28920  * @cfg {Number} longitude Position when init default 0
28921  * @cfg {Number} zoom default 15
28922  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28923  * @cfg {Boolean} mapTypeControl default false
28924  * @cfg {Boolean} disableDoubleClickZoom default false
28925  * @cfg {Boolean} scrollwheel default true
28926  * @cfg {Boolean} streetViewControl default false
28927  * @cfg {Number} radius default 0
28928  * @cfg {String} locationName
28929  * @cfg {Boolean} draggable default true
28930  * @cfg {Boolean} enableAutocomplete default false
28931  * @cfg {Boolean} enableReverseGeocode default true
28932  * @cfg {String} markerTitle
28933  * 
28934  * @constructor
28935  * Create a new LocationPicker
28936  * @param {Object} config The config object
28937  */
28938
28939
28940 Roo.bootstrap.LocationPicker = function(config){
28941     
28942     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28943     
28944     this.addEvents({
28945         /**
28946          * @event initial
28947          * Fires when the picker initialized.
28948          * @param {Roo.bootstrap.LocationPicker} this
28949          * @param {Google Location} location
28950          */
28951         initial : true,
28952         /**
28953          * @event positionchanged
28954          * Fires when the picker position changed.
28955          * @param {Roo.bootstrap.LocationPicker} this
28956          * @param {Google Location} location
28957          */
28958         positionchanged : true,
28959         /**
28960          * @event resize
28961          * Fires when the map resize.
28962          * @param {Roo.bootstrap.LocationPicker} this
28963          */
28964         resize : true,
28965         /**
28966          * @event show
28967          * Fires when the map show.
28968          * @param {Roo.bootstrap.LocationPicker} this
28969          */
28970         show : true,
28971         /**
28972          * @event hide
28973          * Fires when the map hide.
28974          * @param {Roo.bootstrap.LocationPicker} this
28975          */
28976         hide : true,
28977         /**
28978          * @event mapClick
28979          * Fires when click the map.
28980          * @param {Roo.bootstrap.LocationPicker} this
28981          * @param {Map event} e
28982          */
28983         mapClick : true,
28984         /**
28985          * @event mapRightClick
28986          * Fires when right click the map.
28987          * @param {Roo.bootstrap.LocationPicker} this
28988          * @param {Map event} e
28989          */
28990         mapRightClick : true,
28991         /**
28992          * @event markerClick
28993          * Fires when click the marker.
28994          * @param {Roo.bootstrap.LocationPicker} this
28995          * @param {Map event} e
28996          */
28997         markerClick : true,
28998         /**
28999          * @event markerRightClick
29000          * Fires when right click the marker.
29001          * @param {Roo.bootstrap.LocationPicker} this
29002          * @param {Map event} e
29003          */
29004         markerRightClick : true,
29005         /**
29006          * @event OverlayViewDraw
29007          * Fires when OverlayView Draw
29008          * @param {Roo.bootstrap.LocationPicker} this
29009          */
29010         OverlayViewDraw : true,
29011         /**
29012          * @event OverlayViewOnAdd
29013          * Fires when OverlayView Draw
29014          * @param {Roo.bootstrap.LocationPicker} this
29015          */
29016         OverlayViewOnAdd : true,
29017         /**
29018          * @event OverlayViewOnRemove
29019          * Fires when OverlayView Draw
29020          * @param {Roo.bootstrap.LocationPicker} this
29021          */
29022         OverlayViewOnRemove : true,
29023         /**
29024          * @event OverlayViewShow
29025          * Fires when OverlayView Draw
29026          * @param {Roo.bootstrap.LocationPicker} this
29027          * @param {Pixel} cpx
29028          */
29029         OverlayViewShow : true,
29030         /**
29031          * @event OverlayViewHide
29032          * Fires when OverlayView Draw
29033          * @param {Roo.bootstrap.LocationPicker} this
29034          */
29035         OverlayViewHide : true,
29036         /**
29037          * @event loadexception
29038          * Fires when load google lib failed.
29039          * @param {Roo.bootstrap.LocationPicker} this
29040          */
29041         loadexception : true
29042     });
29043         
29044 };
29045
29046 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29047     
29048     gMapContext: false,
29049     
29050     latitude: 0,
29051     longitude: 0,
29052     zoom: 15,
29053     mapTypeId: false,
29054     mapTypeControl: false,
29055     disableDoubleClickZoom: false,
29056     scrollwheel: true,
29057     streetViewControl: false,
29058     radius: 0,
29059     locationName: '',
29060     draggable: true,
29061     enableAutocomplete: false,
29062     enableReverseGeocode: true,
29063     markerTitle: '',
29064     
29065     getAutoCreate: function()
29066     {
29067
29068         var cfg = {
29069             tag: 'div',
29070             cls: 'roo-location-picker'
29071         };
29072         
29073         return cfg
29074     },
29075     
29076     initEvents: function(ct, position)
29077     {       
29078         if(!this.el.getWidth() || this.isApplied()){
29079             return;
29080         }
29081         
29082         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29083         
29084         this.initial();
29085     },
29086     
29087     initial: function()
29088     {
29089         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29090             this.fireEvent('loadexception', this);
29091             return;
29092         }
29093         
29094         if(!this.mapTypeId){
29095             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29096         }
29097         
29098         this.gMapContext = this.GMapContext();
29099         
29100         this.initOverlayView();
29101         
29102         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29103         
29104         var _this = this;
29105                 
29106         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29107             _this.setPosition(_this.gMapContext.marker.position);
29108         });
29109         
29110         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29111             _this.fireEvent('mapClick', this, event);
29112             
29113         });
29114
29115         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29116             _this.fireEvent('mapRightClick', this, event);
29117             
29118         });
29119         
29120         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29121             _this.fireEvent('markerClick', this, event);
29122             
29123         });
29124
29125         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29126             _this.fireEvent('markerRightClick', this, event);
29127             
29128         });
29129         
29130         this.setPosition(this.gMapContext.location);
29131         
29132         this.fireEvent('initial', this, this.gMapContext.location);
29133     },
29134     
29135     initOverlayView: function()
29136     {
29137         var _this = this;
29138         
29139         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29140             
29141             draw: function()
29142             {
29143                 _this.fireEvent('OverlayViewDraw', _this);
29144             },
29145             
29146             onAdd: function()
29147             {
29148                 _this.fireEvent('OverlayViewOnAdd', _this);
29149             },
29150             
29151             onRemove: function()
29152             {
29153                 _this.fireEvent('OverlayViewOnRemove', _this);
29154             },
29155             
29156             show: function(cpx)
29157             {
29158                 _this.fireEvent('OverlayViewShow', _this, cpx);
29159             },
29160             
29161             hide: function()
29162             {
29163                 _this.fireEvent('OverlayViewHide', _this);
29164             }
29165             
29166         });
29167     },
29168     
29169     fromLatLngToContainerPixel: function(event)
29170     {
29171         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29172     },
29173     
29174     isApplied: function() 
29175     {
29176         return this.getGmapContext() == false ? false : true;
29177     },
29178     
29179     getGmapContext: function() 
29180     {
29181         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29182     },
29183     
29184     GMapContext: function() 
29185     {
29186         var position = new google.maps.LatLng(this.latitude, this.longitude);
29187         
29188         var _map = new google.maps.Map(this.el.dom, {
29189             center: position,
29190             zoom: this.zoom,
29191             mapTypeId: this.mapTypeId,
29192             mapTypeControl: this.mapTypeControl,
29193             disableDoubleClickZoom: this.disableDoubleClickZoom,
29194             scrollwheel: this.scrollwheel,
29195             streetViewControl: this.streetViewControl,
29196             locationName: this.locationName,
29197             draggable: this.draggable,
29198             enableAutocomplete: this.enableAutocomplete,
29199             enableReverseGeocode: this.enableReverseGeocode
29200         });
29201         
29202         var _marker = new google.maps.Marker({
29203             position: position,
29204             map: _map,
29205             title: this.markerTitle,
29206             draggable: this.draggable
29207         });
29208         
29209         return {
29210             map: _map,
29211             marker: _marker,
29212             circle: null,
29213             location: position,
29214             radius: this.radius,
29215             locationName: this.locationName,
29216             addressComponents: {
29217                 formatted_address: null,
29218                 addressLine1: null,
29219                 addressLine2: null,
29220                 streetName: null,
29221                 streetNumber: null,
29222                 city: null,
29223                 district: null,
29224                 state: null,
29225                 stateOrProvince: null
29226             },
29227             settings: this,
29228             domContainer: this.el.dom,
29229             geodecoder: new google.maps.Geocoder()
29230         };
29231     },
29232     
29233     drawCircle: function(center, radius, options) 
29234     {
29235         if (this.gMapContext.circle != null) {
29236             this.gMapContext.circle.setMap(null);
29237         }
29238         if (radius > 0) {
29239             radius *= 1;
29240             options = Roo.apply({}, options, {
29241                 strokeColor: "#0000FF",
29242                 strokeOpacity: .35,
29243                 strokeWeight: 2,
29244                 fillColor: "#0000FF",
29245                 fillOpacity: .2
29246             });
29247             
29248             options.map = this.gMapContext.map;
29249             options.radius = radius;
29250             options.center = center;
29251             this.gMapContext.circle = new google.maps.Circle(options);
29252             return this.gMapContext.circle;
29253         }
29254         
29255         return null;
29256     },
29257     
29258     setPosition: function(location) 
29259     {
29260         this.gMapContext.location = location;
29261         this.gMapContext.marker.setPosition(location);
29262         this.gMapContext.map.panTo(location);
29263         this.drawCircle(location, this.gMapContext.radius, {});
29264         
29265         var _this = this;
29266         
29267         if (this.gMapContext.settings.enableReverseGeocode) {
29268             this.gMapContext.geodecoder.geocode({
29269                 latLng: this.gMapContext.location
29270             }, function(results, status) {
29271                 
29272                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29273                     _this.gMapContext.locationName = results[0].formatted_address;
29274                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29275                     
29276                     _this.fireEvent('positionchanged', this, location);
29277                 }
29278             });
29279             
29280             return;
29281         }
29282         
29283         this.fireEvent('positionchanged', this, location);
29284     },
29285     
29286     resize: function()
29287     {
29288         google.maps.event.trigger(this.gMapContext.map, "resize");
29289         
29290         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29291         
29292         this.fireEvent('resize', this);
29293     },
29294     
29295     setPositionByLatLng: function(latitude, longitude)
29296     {
29297         this.setPosition(new google.maps.LatLng(latitude, longitude));
29298     },
29299     
29300     getCurrentPosition: function() 
29301     {
29302         return {
29303             latitude: this.gMapContext.location.lat(),
29304             longitude: this.gMapContext.location.lng()
29305         };
29306     },
29307     
29308     getAddressName: function() 
29309     {
29310         return this.gMapContext.locationName;
29311     },
29312     
29313     getAddressComponents: function() 
29314     {
29315         return this.gMapContext.addressComponents;
29316     },
29317     
29318     address_component_from_google_geocode: function(address_components) 
29319     {
29320         var result = {};
29321         
29322         for (var i = 0; i < address_components.length; i++) {
29323             var component = address_components[i];
29324             if (component.types.indexOf("postal_code") >= 0) {
29325                 result.postalCode = component.short_name;
29326             } else if (component.types.indexOf("street_number") >= 0) {
29327                 result.streetNumber = component.short_name;
29328             } else if (component.types.indexOf("route") >= 0) {
29329                 result.streetName = component.short_name;
29330             } else if (component.types.indexOf("neighborhood") >= 0) {
29331                 result.city = component.short_name;
29332             } else if (component.types.indexOf("locality") >= 0) {
29333                 result.city = component.short_name;
29334             } else if (component.types.indexOf("sublocality") >= 0) {
29335                 result.district = component.short_name;
29336             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29337                 result.stateOrProvince = component.short_name;
29338             } else if (component.types.indexOf("country") >= 0) {
29339                 result.country = component.short_name;
29340             }
29341         }
29342         
29343         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29344         result.addressLine2 = "";
29345         return result;
29346     },
29347     
29348     setZoomLevel: function(zoom)
29349     {
29350         this.gMapContext.map.setZoom(zoom);
29351     },
29352     
29353     show: function()
29354     {
29355         if(!this.el){
29356             return;
29357         }
29358         
29359         this.el.show();
29360         
29361         this.resize();
29362         
29363         this.fireEvent('show', this);
29364     },
29365     
29366     hide: function()
29367     {
29368         if(!this.el){
29369             return;
29370         }
29371         
29372         this.el.hide();
29373         
29374         this.fireEvent('hide', this);
29375     }
29376     
29377 });
29378
29379 Roo.apply(Roo.bootstrap.LocationPicker, {
29380     
29381     OverlayView : function(map, options)
29382     {
29383         options = options || {};
29384         
29385         this.setMap(map);
29386     }
29387     
29388     
29389 });/**
29390  * @class Roo.bootstrap.Alert
29391  * @extends Roo.bootstrap.Component
29392  * Bootstrap Alert class - shows an alert area box
29393  * eg
29394  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29395   Enter a valid email address
29396 </div>
29397  * @licence LGPL
29398  * @cfg {String} title The title of alert
29399  * @cfg {String} html The content of alert
29400  * @cfg {String} weight (  success | info | warning | danger )
29401  * @cfg {String} faicon font-awesomeicon
29402  * 
29403  * @constructor
29404  * Create a new alert
29405  * @param {Object} config The config object
29406  */
29407
29408
29409 Roo.bootstrap.Alert = function(config){
29410     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29411     
29412 };
29413
29414 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29415     
29416     title: '',
29417     html: '',
29418     weight: false,
29419     faicon: false,
29420     
29421     getAutoCreate : function()
29422     {
29423         
29424         var cfg = {
29425             tag : 'div',
29426             cls : 'alert',
29427             cn : [
29428                 {
29429                     tag : 'i',
29430                     cls : 'roo-alert-icon'
29431                     
29432                 },
29433                 {
29434                     tag : 'b',
29435                     cls : 'roo-alert-title',
29436                     html : this.title
29437                 },
29438                 {
29439                     tag : 'span',
29440                     cls : 'roo-alert-text',
29441                     html : this.html
29442                 }
29443             ]
29444         };
29445         
29446         if(this.faicon){
29447             cfg.cn[0].cls += ' fa ' + this.faicon;
29448         }
29449         
29450         if(this.weight){
29451             cfg.cls += ' alert-' + this.weight;
29452         }
29453         
29454         return cfg;
29455     },
29456     
29457     initEvents: function() 
29458     {
29459         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29460     },
29461     
29462     setTitle : function(str)
29463     {
29464         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29465     },
29466     
29467     setText : function(str)
29468     {
29469         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29470     },
29471     
29472     setWeight : function(weight)
29473     {
29474         if(this.weight){
29475             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29476         }
29477         
29478         this.weight = weight;
29479         
29480         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29481     },
29482     
29483     setIcon : function(icon)
29484     {
29485         if(this.faicon){
29486             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29487         }
29488         
29489         this.faicon = icon;
29490         
29491         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29492     },
29493     
29494     hide: function() 
29495     {
29496         this.el.hide();   
29497     },
29498     
29499     show: function() 
29500     {  
29501         this.el.show();   
29502     }
29503     
29504 });
29505
29506  
29507 /*
29508 * Licence: LGPL
29509 */
29510
29511 /**
29512  * @class Roo.bootstrap.UploadCropbox
29513  * @extends Roo.bootstrap.Component
29514  * Bootstrap UploadCropbox class
29515  * @cfg {String} emptyText show when image has been loaded
29516  * @cfg {String} rotateNotify show when image too small to rotate
29517  * @cfg {Number} errorTimeout default 3000
29518  * @cfg {Number} minWidth default 300
29519  * @cfg {Number} minHeight default 300
29520  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29521  * @cfg {Boolean} isDocument (true|false) default false
29522  * @cfg {String} url action url
29523  * @cfg {String} paramName default 'imageUpload'
29524  * @cfg {String} method default POST
29525  * @cfg {Boolean} loadMask (true|false) default true
29526  * @cfg {Boolean} loadingText default 'Loading...'
29527  * 
29528  * @constructor
29529  * Create a new UploadCropbox
29530  * @param {Object} config The config object
29531  */
29532
29533 Roo.bootstrap.UploadCropbox = function(config){
29534     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29535     
29536     this.addEvents({
29537         /**
29538          * @event beforeselectfile
29539          * Fire before select file
29540          * @param {Roo.bootstrap.UploadCropbox} this
29541          */
29542         "beforeselectfile" : true,
29543         /**
29544          * @event initial
29545          * Fire after initEvent
29546          * @param {Roo.bootstrap.UploadCropbox} this
29547          */
29548         "initial" : true,
29549         /**
29550          * @event crop
29551          * Fire after initEvent
29552          * @param {Roo.bootstrap.UploadCropbox} this
29553          * @param {String} data
29554          */
29555         "crop" : true,
29556         /**
29557          * @event prepare
29558          * Fire when preparing the file data
29559          * @param {Roo.bootstrap.UploadCropbox} this
29560          * @param {Object} file
29561          */
29562         "prepare" : true,
29563         /**
29564          * @event exception
29565          * Fire when get exception
29566          * @param {Roo.bootstrap.UploadCropbox} this
29567          * @param {XMLHttpRequest} xhr
29568          */
29569         "exception" : true,
29570         /**
29571          * @event beforeloadcanvas
29572          * Fire before load the canvas
29573          * @param {Roo.bootstrap.UploadCropbox} this
29574          * @param {String} src
29575          */
29576         "beforeloadcanvas" : true,
29577         /**
29578          * @event trash
29579          * Fire when trash image
29580          * @param {Roo.bootstrap.UploadCropbox} this
29581          */
29582         "trash" : true,
29583         /**
29584          * @event download
29585          * Fire when download the image
29586          * @param {Roo.bootstrap.UploadCropbox} this
29587          */
29588         "download" : true,
29589         /**
29590          * @event footerbuttonclick
29591          * Fire when footerbuttonclick
29592          * @param {Roo.bootstrap.UploadCropbox} this
29593          * @param {String} type
29594          */
29595         "footerbuttonclick" : true,
29596         /**
29597          * @event resize
29598          * Fire when resize
29599          * @param {Roo.bootstrap.UploadCropbox} this
29600          */
29601         "resize" : true,
29602         /**
29603          * @event rotate
29604          * Fire when rotate the image
29605          * @param {Roo.bootstrap.UploadCropbox} this
29606          * @param {String} pos
29607          */
29608         "rotate" : true,
29609         /**
29610          * @event inspect
29611          * Fire when inspect the file
29612          * @param {Roo.bootstrap.UploadCropbox} this
29613          * @param {Object} file
29614          */
29615         "inspect" : true,
29616         /**
29617          * @event upload
29618          * Fire when xhr upload the file
29619          * @param {Roo.bootstrap.UploadCropbox} this
29620          * @param {Object} data
29621          */
29622         "upload" : true,
29623         /**
29624          * @event arrange
29625          * Fire when arrange the file data
29626          * @param {Roo.bootstrap.UploadCropbox} this
29627          * @param {Object} formData
29628          */
29629         "arrange" : true
29630     });
29631     
29632     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29633 };
29634
29635 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29636     
29637     emptyText : 'Click to upload image',
29638     rotateNotify : 'Image is too small to rotate',
29639     errorTimeout : 3000,
29640     scale : 0,
29641     baseScale : 1,
29642     rotate : 0,
29643     dragable : false,
29644     pinching : false,
29645     mouseX : 0,
29646     mouseY : 0,
29647     cropData : false,
29648     minWidth : 300,
29649     minHeight : 300,
29650     file : false,
29651     exif : {},
29652     baseRotate : 1,
29653     cropType : 'image/jpeg',
29654     buttons : false,
29655     canvasLoaded : false,
29656     isDocument : false,
29657     method : 'POST',
29658     paramName : 'imageUpload',
29659     loadMask : true,
29660     loadingText : 'Loading...',
29661     maskEl : false,
29662     
29663     getAutoCreate : function()
29664     {
29665         var cfg = {
29666             tag : 'div',
29667             cls : 'roo-upload-cropbox',
29668             cn : [
29669                 {
29670                     tag : 'input',
29671                     cls : 'roo-upload-cropbox-selector',
29672                     type : 'file'
29673                 },
29674                 {
29675                     tag : 'div',
29676                     cls : 'roo-upload-cropbox-body',
29677                     style : 'cursor:pointer',
29678                     cn : [
29679                         {
29680                             tag : 'div',
29681                             cls : 'roo-upload-cropbox-preview'
29682                         },
29683                         {
29684                             tag : 'div',
29685                             cls : 'roo-upload-cropbox-thumb'
29686                         },
29687                         {
29688                             tag : 'div',
29689                             cls : 'roo-upload-cropbox-empty-notify',
29690                             html : this.emptyText
29691                         },
29692                         {
29693                             tag : 'div',
29694                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29695                             html : this.rotateNotify
29696                         }
29697                     ]
29698                 },
29699                 {
29700                     tag : 'div',
29701                     cls : 'roo-upload-cropbox-footer',
29702                     cn : {
29703                         tag : 'div',
29704                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29705                         cn : []
29706                     }
29707                 }
29708             ]
29709         };
29710         
29711         return cfg;
29712     },
29713     
29714     onRender : function(ct, position)
29715     {
29716         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29717         
29718         if (this.buttons.length) {
29719             
29720             Roo.each(this.buttons, function(bb) {
29721                 
29722                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29723                 
29724                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29725                 
29726             }, this);
29727         }
29728         
29729         if(this.loadMask){
29730             this.maskEl = this.el;
29731         }
29732     },
29733     
29734     initEvents : function()
29735     {
29736         this.urlAPI = (window.createObjectURL && window) || 
29737                                 (window.URL && URL.revokeObjectURL && URL) || 
29738                                 (window.webkitURL && webkitURL);
29739                         
29740         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29741         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29742         
29743         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29744         this.selectorEl.hide();
29745         
29746         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29747         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748         
29749         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29750         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29751         this.thumbEl.hide();
29752         
29753         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29754         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755         
29756         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29757         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29758         this.errorEl.hide();
29759         
29760         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29761         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29762         this.footerEl.hide();
29763         
29764         this.setThumbBoxSize();
29765         
29766         this.bind();
29767         
29768         this.resize();
29769         
29770         this.fireEvent('initial', this);
29771     },
29772
29773     bind : function()
29774     {
29775         var _this = this;
29776         
29777         window.addEventListener("resize", function() { _this.resize(); } );
29778         
29779         this.bodyEl.on('click', this.beforeSelectFile, this);
29780         
29781         if(Roo.isTouch){
29782             this.bodyEl.on('touchstart', this.onTouchStart, this);
29783             this.bodyEl.on('touchmove', this.onTouchMove, this);
29784             this.bodyEl.on('touchend', this.onTouchEnd, this);
29785         }
29786         
29787         if(!Roo.isTouch){
29788             this.bodyEl.on('mousedown', this.onMouseDown, this);
29789             this.bodyEl.on('mousemove', this.onMouseMove, this);
29790             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29791             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29792             Roo.get(document).on('mouseup', this.onMouseUp, this);
29793         }
29794         
29795         this.selectorEl.on('change', this.onFileSelected, this);
29796     },
29797     
29798     reset : function()
29799     {    
29800         this.scale = 0;
29801         this.baseScale = 1;
29802         this.rotate = 0;
29803         this.baseRotate = 1;
29804         this.dragable = false;
29805         this.pinching = false;
29806         this.mouseX = 0;
29807         this.mouseY = 0;
29808         this.cropData = false;
29809         this.notifyEl.dom.innerHTML = this.emptyText;
29810         
29811         this.selectorEl.dom.value = '';
29812         
29813     },
29814     
29815     resize : function()
29816     {
29817         if(this.fireEvent('resize', this) != false){
29818             this.setThumbBoxPosition();
29819             this.setCanvasPosition();
29820         }
29821     },
29822     
29823     onFooterButtonClick : function(e, el, o, type)
29824     {
29825         switch (type) {
29826             case 'rotate-left' :
29827                 this.onRotateLeft(e);
29828                 break;
29829             case 'rotate-right' :
29830                 this.onRotateRight(e);
29831                 break;
29832             case 'picture' :
29833                 this.beforeSelectFile(e);
29834                 break;
29835             case 'trash' :
29836                 this.trash(e);
29837                 break;
29838             case 'crop' :
29839                 this.crop(e);
29840                 break;
29841             case 'download' :
29842                 this.download(e);
29843                 break;
29844             default :
29845                 break;
29846         }
29847         
29848         this.fireEvent('footerbuttonclick', this, type);
29849     },
29850     
29851     beforeSelectFile : function(e)
29852     {
29853         e.preventDefault();
29854         
29855         if(this.fireEvent('beforeselectfile', this) != false){
29856             this.selectorEl.dom.click();
29857         }
29858     },
29859     
29860     onFileSelected : function(e)
29861     {
29862         e.preventDefault();
29863         
29864         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29865             return;
29866         }
29867         
29868         var file = this.selectorEl.dom.files[0];
29869         
29870         if(this.fireEvent('inspect', this, file) != false){
29871             this.prepare(file);
29872         }
29873         
29874     },
29875     
29876     trash : function(e)
29877     {
29878         this.fireEvent('trash', this);
29879     },
29880     
29881     download : function(e)
29882     {
29883         this.fireEvent('download', this);
29884     },
29885     
29886     loadCanvas : function(src)
29887     {   
29888         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29889             
29890             this.reset();
29891             
29892             this.imageEl = document.createElement('img');
29893             
29894             var _this = this;
29895             
29896             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29897             
29898             this.imageEl.src = src;
29899         }
29900     },
29901     
29902     onLoadCanvas : function()
29903     {   
29904         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29905         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29906         
29907         this.bodyEl.un('click', this.beforeSelectFile, this);
29908         
29909         this.notifyEl.hide();
29910         this.thumbEl.show();
29911         this.footerEl.show();
29912         
29913         this.baseRotateLevel();
29914         
29915         if(this.isDocument){
29916             this.setThumbBoxSize();
29917         }
29918         
29919         this.setThumbBoxPosition();
29920         
29921         this.baseScaleLevel();
29922         
29923         this.draw();
29924         
29925         this.resize();
29926         
29927         this.canvasLoaded = true;
29928         
29929         if(this.loadMask){
29930             this.maskEl.unmask();
29931         }
29932         
29933     },
29934     
29935     setCanvasPosition : function()
29936     {   
29937         if(!this.canvasEl){
29938             return;
29939         }
29940         
29941         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29942         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29943         
29944         this.previewEl.setLeft(pw);
29945         this.previewEl.setTop(ph);
29946         
29947     },
29948     
29949     onMouseDown : function(e)
29950     {   
29951         e.stopEvent();
29952         
29953         this.dragable = true;
29954         this.pinching = false;
29955         
29956         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29957             this.dragable = false;
29958             return;
29959         }
29960         
29961         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29962         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29963         
29964     },
29965     
29966     onMouseMove : function(e)
29967     {   
29968         e.stopEvent();
29969         
29970         if(!this.canvasLoaded){
29971             return;
29972         }
29973         
29974         if (!this.dragable){
29975             return;
29976         }
29977         
29978         var minX = Math.ceil(this.thumbEl.getLeft(true));
29979         var minY = Math.ceil(this.thumbEl.getTop(true));
29980         
29981         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29982         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29983         
29984         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29985         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29986         
29987         x = x - this.mouseX;
29988         y = y - this.mouseY;
29989         
29990         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29991         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29992         
29993         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29994         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29995         
29996         this.previewEl.setLeft(bgX);
29997         this.previewEl.setTop(bgY);
29998         
29999         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30000         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30001     },
30002     
30003     onMouseUp : function(e)
30004     {   
30005         e.stopEvent();
30006         
30007         this.dragable = false;
30008     },
30009     
30010     onMouseWheel : function(e)
30011     {   
30012         e.stopEvent();
30013         
30014         this.startScale = this.scale;
30015         
30016         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30017         
30018         if(!this.zoomable()){
30019             this.scale = this.startScale;
30020             return;
30021         }
30022         
30023         this.draw();
30024         
30025         return;
30026     },
30027     
30028     zoomable : function()
30029     {
30030         var minScale = this.thumbEl.getWidth() / this.minWidth;
30031         
30032         if(this.minWidth < this.minHeight){
30033             minScale = this.thumbEl.getHeight() / this.minHeight;
30034         }
30035         
30036         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30037         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30038         
30039         if(
30040                 this.isDocument &&
30041                 (this.rotate == 0 || this.rotate == 180) && 
30042                 (
30043                     width > this.imageEl.OriginWidth || 
30044                     height > this.imageEl.OriginHeight ||
30045                     (width < this.minWidth && height < this.minHeight)
30046                 )
30047         ){
30048             return false;
30049         }
30050         
30051         if(
30052                 this.isDocument &&
30053                 (this.rotate == 90 || this.rotate == 270) && 
30054                 (
30055                     width > this.imageEl.OriginWidth || 
30056                     height > this.imageEl.OriginHeight ||
30057                     (width < this.minHeight && height < this.minWidth)
30058                 )
30059         ){
30060             return false;
30061         }
30062         
30063         if(
30064                 !this.isDocument &&
30065                 (this.rotate == 0 || this.rotate == 180) && 
30066                 (
30067                     width < this.minWidth || 
30068                     width > this.imageEl.OriginWidth || 
30069                     height < this.minHeight || 
30070                     height > this.imageEl.OriginHeight
30071                 )
30072         ){
30073             return false;
30074         }
30075         
30076         if(
30077                 !this.isDocument &&
30078                 (this.rotate == 90 || this.rotate == 270) && 
30079                 (
30080                     width < this.minHeight || 
30081                     width > this.imageEl.OriginWidth || 
30082                     height < this.minWidth || 
30083                     height > this.imageEl.OriginHeight
30084                 )
30085         ){
30086             return false;
30087         }
30088         
30089         return true;
30090         
30091     },
30092     
30093     onRotateLeft : function(e)
30094     {   
30095         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30096             
30097             var minScale = this.thumbEl.getWidth() / this.minWidth;
30098             
30099             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30100             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30101             
30102             this.startScale = this.scale;
30103             
30104             while (this.getScaleLevel() < minScale){
30105             
30106                 this.scale = this.scale + 1;
30107                 
30108                 if(!this.zoomable()){
30109                     break;
30110                 }
30111                 
30112                 if(
30113                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30114                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30115                 ){
30116                     continue;
30117                 }
30118                 
30119                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30120
30121                 this.draw();
30122                 
30123                 return;
30124             }
30125             
30126             this.scale = this.startScale;
30127             
30128             this.onRotateFail();
30129             
30130             return false;
30131         }
30132         
30133         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30134
30135         if(this.isDocument){
30136             this.setThumbBoxSize();
30137             this.setThumbBoxPosition();
30138             this.setCanvasPosition();
30139         }
30140         
30141         this.draw();
30142         
30143         this.fireEvent('rotate', this, 'left');
30144         
30145     },
30146     
30147     onRotateRight : function(e)
30148     {
30149         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30150             
30151             var minScale = this.thumbEl.getWidth() / this.minWidth;
30152         
30153             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30154             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30155             
30156             this.startScale = this.scale;
30157             
30158             while (this.getScaleLevel() < minScale){
30159             
30160                 this.scale = this.scale + 1;
30161                 
30162                 if(!this.zoomable()){
30163                     break;
30164                 }
30165                 
30166                 if(
30167                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30168                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30169                 ){
30170                     continue;
30171                 }
30172                 
30173                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30174
30175                 this.draw();
30176                 
30177                 return;
30178             }
30179             
30180             this.scale = this.startScale;
30181             
30182             this.onRotateFail();
30183             
30184             return false;
30185         }
30186         
30187         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30188
30189         if(this.isDocument){
30190             this.setThumbBoxSize();
30191             this.setThumbBoxPosition();
30192             this.setCanvasPosition();
30193         }
30194         
30195         this.draw();
30196         
30197         this.fireEvent('rotate', this, 'right');
30198     },
30199     
30200     onRotateFail : function()
30201     {
30202         this.errorEl.show(true);
30203         
30204         var _this = this;
30205         
30206         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30207     },
30208     
30209     draw : function()
30210     {
30211         this.previewEl.dom.innerHTML = '';
30212         
30213         var canvasEl = document.createElement("canvas");
30214         
30215         var contextEl = canvasEl.getContext("2d");
30216         
30217         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30218         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30219         var center = this.imageEl.OriginWidth / 2;
30220         
30221         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30222             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30223             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30224             center = this.imageEl.OriginHeight / 2;
30225         }
30226         
30227         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30228         
30229         contextEl.translate(center, center);
30230         contextEl.rotate(this.rotate * Math.PI / 180);
30231
30232         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30233         
30234         this.canvasEl = document.createElement("canvas");
30235         
30236         this.contextEl = this.canvasEl.getContext("2d");
30237         
30238         switch (this.rotate) {
30239             case 0 :
30240                 
30241                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30242                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30243                 
30244                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30245                 
30246                 break;
30247             case 90 : 
30248                 
30249                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30250                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30251                 
30252                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30253                     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);
30254                     break;
30255                 }
30256                 
30257                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30258                 
30259                 break;
30260             case 180 :
30261                 
30262                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30263                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30264                 
30265                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30266                     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);
30267                     break;
30268                 }
30269                 
30270                 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);
30271                 
30272                 break;
30273             case 270 :
30274                 
30275                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30276                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30277         
30278                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30279                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30280                     break;
30281                 }
30282                 
30283                 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);
30284                 
30285                 break;
30286             default : 
30287                 break;
30288         }
30289         
30290         this.previewEl.appendChild(this.canvasEl);
30291         
30292         this.setCanvasPosition();
30293     },
30294     
30295     crop : function()
30296     {
30297         if(!this.canvasLoaded){
30298             return;
30299         }
30300         
30301         var imageCanvas = document.createElement("canvas");
30302         
30303         var imageContext = imageCanvas.getContext("2d");
30304         
30305         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30306         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30307         
30308         var center = imageCanvas.width / 2;
30309         
30310         imageContext.translate(center, center);
30311         
30312         imageContext.rotate(this.rotate * Math.PI / 180);
30313         
30314         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30315         
30316         var canvas = document.createElement("canvas");
30317         
30318         var context = canvas.getContext("2d");
30319                 
30320         canvas.width = this.minWidth;
30321         canvas.height = this.minHeight;
30322
30323         switch (this.rotate) {
30324             case 0 :
30325                 
30326                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30327                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30328                 
30329                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30330                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30331                 
30332                 var targetWidth = this.minWidth - 2 * x;
30333                 var targetHeight = this.minHeight - 2 * y;
30334                 
30335                 var scale = 1;
30336                 
30337                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30338                     scale = targetWidth / width;
30339                 }
30340                 
30341                 if(x > 0 && y == 0){
30342                     scale = targetHeight / height;
30343                 }
30344                 
30345                 if(x > 0 && y > 0){
30346                     scale = targetWidth / width;
30347                     
30348                     if(width < height){
30349                         scale = targetHeight / height;
30350                     }
30351                 }
30352                 
30353                 context.scale(scale, scale);
30354                 
30355                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30356                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30357
30358                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30359                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30360
30361                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30362                 
30363                 break;
30364             case 90 : 
30365                 
30366                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30367                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30368                 
30369                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30370                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30371                 
30372                 var targetWidth = this.minWidth - 2 * x;
30373                 var targetHeight = this.minHeight - 2 * y;
30374                 
30375                 var scale = 1;
30376                 
30377                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30378                     scale = targetWidth / width;
30379                 }
30380                 
30381                 if(x > 0 && y == 0){
30382                     scale = targetHeight / height;
30383                 }
30384                 
30385                 if(x > 0 && y > 0){
30386                     scale = targetWidth / width;
30387                     
30388                     if(width < height){
30389                         scale = targetHeight / height;
30390                     }
30391                 }
30392                 
30393                 context.scale(scale, scale);
30394                 
30395                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30396                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30397
30398                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30399                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30400                 
30401                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30402                 
30403                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30404                 
30405                 break;
30406             case 180 :
30407                 
30408                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30409                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30410                 
30411                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30412                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30413                 
30414                 var targetWidth = this.minWidth - 2 * x;
30415                 var targetHeight = this.minHeight - 2 * y;
30416                 
30417                 var scale = 1;
30418                 
30419                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30420                     scale = targetWidth / width;
30421                 }
30422                 
30423                 if(x > 0 && y == 0){
30424                     scale = targetHeight / height;
30425                 }
30426                 
30427                 if(x > 0 && y > 0){
30428                     scale = targetWidth / width;
30429                     
30430                     if(width < height){
30431                         scale = targetHeight / height;
30432                     }
30433                 }
30434                 
30435                 context.scale(scale, scale);
30436                 
30437                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30438                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30439
30440                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30441                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30442
30443                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30444                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30445                 
30446                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30447                 
30448                 break;
30449             case 270 :
30450                 
30451                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30452                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30453                 
30454                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30455                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30456                 
30457                 var targetWidth = this.minWidth - 2 * x;
30458                 var targetHeight = this.minHeight - 2 * y;
30459                 
30460                 var scale = 1;
30461                 
30462                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30463                     scale = targetWidth / width;
30464                 }
30465                 
30466                 if(x > 0 && y == 0){
30467                     scale = targetHeight / height;
30468                 }
30469                 
30470                 if(x > 0 && y > 0){
30471                     scale = targetWidth / width;
30472                     
30473                     if(width < height){
30474                         scale = targetHeight / height;
30475                     }
30476                 }
30477                 
30478                 context.scale(scale, scale);
30479                 
30480                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30481                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30482
30483                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30484                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30485                 
30486                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30487                 
30488                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30489                 
30490                 break;
30491             default : 
30492                 break;
30493         }
30494         
30495         this.cropData = canvas.toDataURL(this.cropType);
30496         
30497         if(this.fireEvent('crop', this, this.cropData) !== false){
30498             this.process(this.file, this.cropData);
30499         }
30500         
30501         return;
30502         
30503     },
30504     
30505     setThumbBoxSize : function()
30506     {
30507         var width, height;
30508         
30509         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30510             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30511             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30512             
30513             this.minWidth = width;
30514             this.minHeight = height;
30515             
30516             if(this.rotate == 90 || this.rotate == 270){
30517                 this.minWidth = height;
30518                 this.minHeight = width;
30519             }
30520         }
30521         
30522         height = 300;
30523         width = Math.ceil(this.minWidth * height / this.minHeight);
30524         
30525         if(this.minWidth > this.minHeight){
30526             width = 300;
30527             height = Math.ceil(this.minHeight * width / this.minWidth);
30528         }
30529         
30530         this.thumbEl.setStyle({
30531             width : width + 'px',
30532             height : height + 'px'
30533         });
30534
30535         return;
30536             
30537     },
30538     
30539     setThumbBoxPosition : function()
30540     {
30541         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30542         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30543         
30544         this.thumbEl.setLeft(x);
30545         this.thumbEl.setTop(y);
30546         
30547     },
30548     
30549     baseRotateLevel : function()
30550     {
30551         this.baseRotate = 1;
30552         
30553         if(
30554                 typeof(this.exif) != 'undefined' &&
30555                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30556                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30557         ){
30558             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30559         }
30560         
30561         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30562         
30563     },
30564     
30565     baseScaleLevel : function()
30566     {
30567         var width, height;
30568         
30569         if(this.isDocument){
30570             
30571             if(this.baseRotate == 6 || this.baseRotate == 8){
30572             
30573                 height = this.thumbEl.getHeight();
30574                 this.baseScale = height / this.imageEl.OriginWidth;
30575
30576                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30577                     width = this.thumbEl.getWidth();
30578                     this.baseScale = width / this.imageEl.OriginHeight;
30579                 }
30580
30581                 return;
30582             }
30583
30584             height = this.thumbEl.getHeight();
30585             this.baseScale = height / this.imageEl.OriginHeight;
30586
30587             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30588                 width = this.thumbEl.getWidth();
30589                 this.baseScale = width / this.imageEl.OriginWidth;
30590             }
30591
30592             return;
30593         }
30594         
30595         if(this.baseRotate == 6 || this.baseRotate == 8){
30596             
30597             width = this.thumbEl.getHeight();
30598             this.baseScale = width / this.imageEl.OriginHeight;
30599             
30600             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30601                 height = this.thumbEl.getWidth();
30602                 this.baseScale = height / this.imageEl.OriginHeight;
30603             }
30604             
30605             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30606                 height = this.thumbEl.getWidth();
30607                 this.baseScale = height / this.imageEl.OriginHeight;
30608                 
30609                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30610                     width = this.thumbEl.getHeight();
30611                     this.baseScale = width / this.imageEl.OriginWidth;
30612                 }
30613             }
30614             
30615             return;
30616         }
30617         
30618         width = this.thumbEl.getWidth();
30619         this.baseScale = width / this.imageEl.OriginWidth;
30620         
30621         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30622             height = this.thumbEl.getHeight();
30623             this.baseScale = height / this.imageEl.OriginHeight;
30624         }
30625         
30626         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30627             
30628             height = this.thumbEl.getHeight();
30629             this.baseScale = height / this.imageEl.OriginHeight;
30630             
30631             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30632                 width = this.thumbEl.getWidth();
30633                 this.baseScale = width / this.imageEl.OriginWidth;
30634             }
30635             
30636         }
30637         
30638         return;
30639     },
30640     
30641     getScaleLevel : function()
30642     {
30643         return this.baseScale * Math.pow(1.1, this.scale);
30644     },
30645     
30646     onTouchStart : function(e)
30647     {
30648         if(!this.canvasLoaded){
30649             this.beforeSelectFile(e);
30650             return;
30651         }
30652         
30653         var touches = e.browserEvent.touches;
30654         
30655         if(!touches){
30656             return;
30657         }
30658         
30659         if(touches.length == 1){
30660             this.onMouseDown(e);
30661             return;
30662         }
30663         
30664         if(touches.length != 2){
30665             return;
30666         }
30667         
30668         var coords = [];
30669         
30670         for(var i = 0, finger; finger = touches[i]; i++){
30671             coords.push(finger.pageX, finger.pageY);
30672         }
30673         
30674         var x = Math.pow(coords[0] - coords[2], 2);
30675         var y = Math.pow(coords[1] - coords[3], 2);
30676         
30677         this.startDistance = Math.sqrt(x + y);
30678         
30679         this.startScale = this.scale;
30680         
30681         this.pinching = true;
30682         this.dragable = false;
30683         
30684     },
30685     
30686     onTouchMove : function(e)
30687     {
30688         if(!this.pinching && !this.dragable){
30689             return;
30690         }
30691         
30692         var touches = e.browserEvent.touches;
30693         
30694         if(!touches){
30695             return;
30696         }
30697         
30698         if(this.dragable){
30699             this.onMouseMove(e);
30700             return;
30701         }
30702         
30703         var coords = [];
30704         
30705         for(var i = 0, finger; finger = touches[i]; i++){
30706             coords.push(finger.pageX, finger.pageY);
30707         }
30708         
30709         var x = Math.pow(coords[0] - coords[2], 2);
30710         var y = Math.pow(coords[1] - coords[3], 2);
30711         
30712         this.endDistance = Math.sqrt(x + y);
30713         
30714         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30715         
30716         if(!this.zoomable()){
30717             this.scale = this.startScale;
30718             return;
30719         }
30720         
30721         this.draw();
30722         
30723     },
30724     
30725     onTouchEnd : function(e)
30726     {
30727         this.pinching = false;
30728         this.dragable = false;
30729         
30730     },
30731     
30732     process : function(file, crop)
30733     {
30734         if(this.loadMask){
30735             this.maskEl.mask(this.loadingText);
30736         }
30737         
30738         this.xhr = new XMLHttpRequest();
30739         
30740         file.xhr = this.xhr;
30741
30742         this.xhr.open(this.method, this.url, true);
30743         
30744         var headers = {
30745             "Accept": "application/json",
30746             "Cache-Control": "no-cache",
30747             "X-Requested-With": "XMLHttpRequest"
30748         };
30749         
30750         for (var headerName in headers) {
30751             var headerValue = headers[headerName];
30752             if (headerValue) {
30753                 this.xhr.setRequestHeader(headerName, headerValue);
30754             }
30755         }
30756         
30757         var _this = this;
30758         
30759         this.xhr.onload = function()
30760         {
30761             _this.xhrOnLoad(_this.xhr);
30762         }
30763         
30764         this.xhr.onerror = function()
30765         {
30766             _this.xhrOnError(_this.xhr);
30767         }
30768         
30769         var formData = new FormData();
30770
30771         formData.append('returnHTML', 'NO');
30772         
30773         if(crop){
30774             formData.append('crop', crop);
30775         }
30776         
30777         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30778             formData.append(this.paramName, file, file.name);
30779         }
30780         
30781         if(typeof(file.filename) != 'undefined'){
30782             formData.append('filename', file.filename);
30783         }
30784         
30785         if(typeof(file.mimetype) != 'undefined'){
30786             formData.append('mimetype', file.mimetype);
30787         }
30788         
30789         if(this.fireEvent('arrange', this, formData) != false){
30790             this.xhr.send(formData);
30791         };
30792     },
30793     
30794     xhrOnLoad : function(xhr)
30795     {
30796         if(this.loadMask){
30797             this.maskEl.unmask();
30798         }
30799         
30800         if (xhr.readyState !== 4) {
30801             this.fireEvent('exception', this, xhr);
30802             return;
30803         }
30804
30805         var response = Roo.decode(xhr.responseText);
30806         
30807         if(!response.success){
30808             this.fireEvent('exception', this, xhr);
30809             return;
30810         }
30811         
30812         var response = Roo.decode(xhr.responseText);
30813         
30814         this.fireEvent('upload', this, response);
30815         
30816     },
30817     
30818     xhrOnError : function()
30819     {
30820         if(this.loadMask){
30821             this.maskEl.unmask();
30822         }
30823         
30824         Roo.log('xhr on error');
30825         
30826         var response = Roo.decode(xhr.responseText);
30827           
30828         Roo.log(response);
30829         
30830     },
30831     
30832     prepare : function(file)
30833     {   
30834         if(this.loadMask){
30835             this.maskEl.mask(this.loadingText);
30836         }
30837         
30838         this.file = false;
30839         this.exif = {};
30840         
30841         if(typeof(file) === 'string'){
30842             this.loadCanvas(file);
30843             return;
30844         }
30845         
30846         if(!file || !this.urlAPI){
30847             return;
30848         }
30849         
30850         this.file = file;
30851         this.cropType = file.type;
30852         
30853         var _this = this;
30854         
30855         if(this.fireEvent('prepare', this, this.file) != false){
30856             
30857             var reader = new FileReader();
30858             
30859             reader.onload = function (e) {
30860                 if (e.target.error) {
30861                     Roo.log(e.target.error);
30862                     return;
30863                 }
30864                 
30865                 var buffer = e.target.result,
30866                     dataView = new DataView(buffer),
30867                     offset = 2,
30868                     maxOffset = dataView.byteLength - 4,
30869                     markerBytes,
30870                     markerLength;
30871                 
30872                 if (dataView.getUint16(0) === 0xffd8) {
30873                     while (offset < maxOffset) {
30874                         markerBytes = dataView.getUint16(offset);
30875                         
30876                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30877                             markerLength = dataView.getUint16(offset + 2) + 2;
30878                             if (offset + markerLength > dataView.byteLength) {
30879                                 Roo.log('Invalid meta data: Invalid segment size.');
30880                                 break;
30881                             }
30882                             
30883                             if(markerBytes == 0xffe1){
30884                                 _this.parseExifData(
30885                                     dataView,
30886                                     offset,
30887                                     markerLength
30888                                 );
30889                             }
30890                             
30891                             offset += markerLength;
30892                             
30893                             continue;
30894                         }
30895                         
30896                         break;
30897                     }
30898                     
30899                 }
30900                 
30901                 var url = _this.urlAPI.createObjectURL(_this.file);
30902                 
30903                 _this.loadCanvas(url);
30904                 
30905                 return;
30906             }
30907             
30908             reader.readAsArrayBuffer(this.file);
30909             
30910         }
30911         
30912     },
30913     
30914     parseExifData : function(dataView, offset, length)
30915     {
30916         var tiffOffset = offset + 10,
30917             littleEndian,
30918             dirOffset;
30919     
30920         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30921             // No Exif data, might be XMP data instead
30922             return;
30923         }
30924         
30925         // Check for the ASCII code for "Exif" (0x45786966):
30926         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30927             // No Exif data, might be XMP data instead
30928             return;
30929         }
30930         if (tiffOffset + 8 > dataView.byteLength) {
30931             Roo.log('Invalid Exif data: Invalid segment size.');
30932             return;
30933         }
30934         // Check for the two null bytes:
30935         if (dataView.getUint16(offset + 8) !== 0x0000) {
30936             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30937             return;
30938         }
30939         // Check the byte alignment:
30940         switch (dataView.getUint16(tiffOffset)) {
30941         case 0x4949:
30942             littleEndian = true;
30943             break;
30944         case 0x4D4D:
30945             littleEndian = false;
30946             break;
30947         default:
30948             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30949             return;
30950         }
30951         // Check for the TIFF tag marker (0x002A):
30952         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30953             Roo.log('Invalid Exif data: Missing TIFF marker.');
30954             return;
30955         }
30956         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30957         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30958         
30959         this.parseExifTags(
30960             dataView,
30961             tiffOffset,
30962             tiffOffset + dirOffset,
30963             littleEndian
30964         );
30965     },
30966     
30967     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30968     {
30969         var tagsNumber,
30970             dirEndOffset,
30971             i;
30972         if (dirOffset + 6 > dataView.byteLength) {
30973             Roo.log('Invalid Exif data: Invalid directory offset.');
30974             return;
30975         }
30976         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30977         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30978         if (dirEndOffset + 4 > dataView.byteLength) {
30979             Roo.log('Invalid Exif data: Invalid directory size.');
30980             return;
30981         }
30982         for (i = 0; i < tagsNumber; i += 1) {
30983             this.parseExifTag(
30984                 dataView,
30985                 tiffOffset,
30986                 dirOffset + 2 + 12 * i, // tag offset
30987                 littleEndian
30988             );
30989         }
30990         // Return the offset to the next directory:
30991         return dataView.getUint32(dirEndOffset, littleEndian);
30992     },
30993     
30994     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30995     {
30996         var tag = dataView.getUint16(offset, littleEndian);
30997         
30998         this.exif[tag] = this.getExifValue(
30999             dataView,
31000             tiffOffset,
31001             offset,
31002             dataView.getUint16(offset + 2, littleEndian), // tag type
31003             dataView.getUint32(offset + 4, littleEndian), // tag length
31004             littleEndian
31005         );
31006     },
31007     
31008     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31009     {
31010         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31011             tagSize,
31012             dataOffset,
31013             values,
31014             i,
31015             str,
31016             c;
31017     
31018         if (!tagType) {
31019             Roo.log('Invalid Exif data: Invalid tag type.');
31020             return;
31021         }
31022         
31023         tagSize = tagType.size * length;
31024         // Determine if the value is contained in the dataOffset bytes,
31025         // or if the value at the dataOffset is a pointer to the actual data:
31026         dataOffset = tagSize > 4 ?
31027                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31028         if (dataOffset + tagSize > dataView.byteLength) {
31029             Roo.log('Invalid Exif data: Invalid data offset.');
31030             return;
31031         }
31032         if (length === 1) {
31033             return tagType.getValue(dataView, dataOffset, littleEndian);
31034         }
31035         values = [];
31036         for (i = 0; i < length; i += 1) {
31037             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31038         }
31039         
31040         if (tagType.ascii) {
31041             str = '';
31042             // Concatenate the chars:
31043             for (i = 0; i < values.length; i += 1) {
31044                 c = values[i];
31045                 // Ignore the terminating NULL byte(s):
31046                 if (c === '\u0000') {
31047                     break;
31048                 }
31049                 str += c;
31050             }
31051             return str;
31052         }
31053         return values;
31054     }
31055     
31056 });
31057
31058 Roo.apply(Roo.bootstrap.UploadCropbox, {
31059     tags : {
31060         'Orientation': 0x0112
31061     },
31062     
31063     Orientation: {
31064             1: 0, //'top-left',
31065 //            2: 'top-right',
31066             3: 180, //'bottom-right',
31067 //            4: 'bottom-left',
31068 //            5: 'left-top',
31069             6: 90, //'right-top',
31070 //            7: 'right-bottom',
31071             8: 270 //'left-bottom'
31072     },
31073     
31074     exifTagTypes : {
31075         // byte, 8-bit unsigned int:
31076         1: {
31077             getValue: function (dataView, dataOffset) {
31078                 return dataView.getUint8(dataOffset);
31079             },
31080             size: 1
31081         },
31082         // ascii, 8-bit byte:
31083         2: {
31084             getValue: function (dataView, dataOffset) {
31085                 return String.fromCharCode(dataView.getUint8(dataOffset));
31086             },
31087             size: 1,
31088             ascii: true
31089         },
31090         // short, 16 bit int:
31091         3: {
31092             getValue: function (dataView, dataOffset, littleEndian) {
31093                 return dataView.getUint16(dataOffset, littleEndian);
31094             },
31095             size: 2
31096         },
31097         // long, 32 bit int:
31098         4: {
31099             getValue: function (dataView, dataOffset, littleEndian) {
31100                 return dataView.getUint32(dataOffset, littleEndian);
31101             },
31102             size: 4
31103         },
31104         // rational = two long values, first is numerator, second is denominator:
31105         5: {
31106             getValue: function (dataView, dataOffset, littleEndian) {
31107                 return dataView.getUint32(dataOffset, littleEndian) /
31108                     dataView.getUint32(dataOffset + 4, littleEndian);
31109             },
31110             size: 8
31111         },
31112         // slong, 32 bit signed int:
31113         9: {
31114             getValue: function (dataView, dataOffset, littleEndian) {
31115                 return dataView.getInt32(dataOffset, littleEndian);
31116             },
31117             size: 4
31118         },
31119         // srational, two slongs, first is numerator, second is denominator:
31120         10: {
31121             getValue: function (dataView, dataOffset, littleEndian) {
31122                 return dataView.getInt32(dataOffset, littleEndian) /
31123                     dataView.getInt32(dataOffset + 4, littleEndian);
31124             },
31125             size: 8
31126         }
31127     },
31128     
31129     footer : {
31130         STANDARD : [
31131             {
31132                 tag : 'div',
31133                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31134                 action : 'rotate-left',
31135                 cn : [
31136                     {
31137                         tag : 'button',
31138                         cls : 'btn btn-default',
31139                         html : '<i class="fa fa-undo"></i>'
31140                     }
31141                 ]
31142             },
31143             {
31144                 tag : 'div',
31145                 cls : 'btn-group roo-upload-cropbox-picture',
31146                 action : 'picture',
31147                 cn : [
31148                     {
31149                         tag : 'button',
31150                         cls : 'btn btn-default',
31151                         html : '<i class="fa fa-picture-o"></i>'
31152                     }
31153                 ]
31154             },
31155             {
31156                 tag : 'div',
31157                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31158                 action : 'rotate-right',
31159                 cn : [
31160                     {
31161                         tag : 'button',
31162                         cls : 'btn btn-default',
31163                         html : '<i class="fa fa-repeat"></i>'
31164                     }
31165                 ]
31166             }
31167         ],
31168         DOCUMENT : [
31169             {
31170                 tag : 'div',
31171                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31172                 action : 'rotate-left',
31173                 cn : [
31174                     {
31175                         tag : 'button',
31176                         cls : 'btn btn-default',
31177                         html : '<i class="fa fa-undo"></i>'
31178                     }
31179                 ]
31180             },
31181             {
31182                 tag : 'div',
31183                 cls : 'btn-group roo-upload-cropbox-download',
31184                 action : 'download',
31185                 cn : [
31186                     {
31187                         tag : 'button',
31188                         cls : 'btn btn-default',
31189                         html : '<i class="fa fa-download"></i>'
31190                     }
31191                 ]
31192             },
31193             {
31194                 tag : 'div',
31195                 cls : 'btn-group roo-upload-cropbox-crop',
31196                 action : 'crop',
31197                 cn : [
31198                     {
31199                         tag : 'button',
31200                         cls : 'btn btn-default',
31201                         html : '<i class="fa fa-crop"></i>'
31202                     }
31203                 ]
31204             },
31205             {
31206                 tag : 'div',
31207                 cls : 'btn-group roo-upload-cropbox-trash',
31208                 action : 'trash',
31209                 cn : [
31210                     {
31211                         tag : 'button',
31212                         cls : 'btn btn-default',
31213                         html : '<i class="fa fa-trash"></i>'
31214                     }
31215                 ]
31216             },
31217             {
31218                 tag : 'div',
31219                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31220                 action : 'rotate-right',
31221                 cn : [
31222                     {
31223                         tag : 'button',
31224                         cls : 'btn btn-default',
31225                         html : '<i class="fa fa-repeat"></i>'
31226                     }
31227                 ]
31228             }
31229         ],
31230         ROTATOR : [
31231             {
31232                 tag : 'div',
31233                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31234                 action : 'rotate-left',
31235                 cn : [
31236                     {
31237                         tag : 'button',
31238                         cls : 'btn btn-default',
31239                         html : '<i class="fa fa-undo"></i>'
31240                     }
31241                 ]
31242             },
31243             {
31244                 tag : 'div',
31245                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31246                 action : 'rotate-right',
31247                 cn : [
31248                     {
31249                         tag : 'button',
31250                         cls : 'btn btn-default',
31251                         html : '<i class="fa fa-repeat"></i>'
31252                     }
31253                 ]
31254             }
31255         ]
31256     }
31257 });
31258
31259 /*
31260 * Licence: LGPL
31261 */
31262
31263 /**
31264  * @class Roo.bootstrap.DocumentManager
31265  * @extends Roo.bootstrap.Component
31266  * Bootstrap DocumentManager class
31267  * @cfg {String} paramName default 'imageUpload'
31268  * @cfg {String} toolTipName default 'filename'
31269  * @cfg {String} method default POST
31270  * @cfg {String} url action url
31271  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31272  * @cfg {Boolean} multiple multiple upload default true
31273  * @cfg {Number} thumbSize default 300
31274  * @cfg {String} fieldLabel
31275  * @cfg {Number} labelWidth default 4
31276  * @cfg {String} labelAlign (left|top) default left
31277  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31278 * @cfg {Number} labellg set the width of label (1-12)
31279  * @cfg {Number} labelmd set the width of label (1-12)
31280  * @cfg {Number} labelsm set the width of label (1-12)
31281  * @cfg {Number} labelxs set the width of label (1-12)
31282  * 
31283  * @constructor
31284  * Create a new DocumentManager
31285  * @param {Object} config The config object
31286  */
31287
31288 Roo.bootstrap.DocumentManager = function(config){
31289     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31290     
31291     this.files = [];
31292     this.delegates = [];
31293     
31294     this.addEvents({
31295         /**
31296          * @event initial
31297          * Fire when initial the DocumentManager
31298          * @param {Roo.bootstrap.DocumentManager} this
31299          */
31300         "initial" : true,
31301         /**
31302          * @event inspect
31303          * inspect selected file
31304          * @param {Roo.bootstrap.DocumentManager} this
31305          * @param {File} file
31306          */
31307         "inspect" : true,
31308         /**
31309          * @event exception
31310          * Fire when xhr load exception
31311          * @param {Roo.bootstrap.DocumentManager} this
31312          * @param {XMLHttpRequest} xhr
31313          */
31314         "exception" : true,
31315         /**
31316          * @event afterupload
31317          * Fire when xhr load exception
31318          * @param {Roo.bootstrap.DocumentManager} this
31319          * @param {XMLHttpRequest} xhr
31320          */
31321         "afterupload" : true,
31322         /**
31323          * @event prepare
31324          * prepare the form data
31325          * @param {Roo.bootstrap.DocumentManager} this
31326          * @param {Object} formData
31327          */
31328         "prepare" : true,
31329         /**
31330          * @event remove
31331          * Fire when remove the file
31332          * @param {Roo.bootstrap.DocumentManager} this
31333          * @param {Object} file
31334          */
31335         "remove" : true,
31336         /**
31337          * @event refresh
31338          * Fire after refresh the file
31339          * @param {Roo.bootstrap.DocumentManager} this
31340          */
31341         "refresh" : true,
31342         /**
31343          * @event click
31344          * Fire after click the image
31345          * @param {Roo.bootstrap.DocumentManager} this
31346          * @param {Object} file
31347          */
31348         "click" : true,
31349         /**
31350          * @event edit
31351          * Fire when upload a image and editable set to true
31352          * @param {Roo.bootstrap.DocumentManager} this
31353          * @param {Object} file
31354          */
31355         "edit" : true,
31356         /**
31357          * @event beforeselectfile
31358          * Fire before select file
31359          * @param {Roo.bootstrap.DocumentManager} this
31360          */
31361         "beforeselectfile" : true,
31362         /**
31363          * @event process
31364          * Fire before process file
31365          * @param {Roo.bootstrap.DocumentManager} this
31366          * @param {Object} file
31367          */
31368         "process" : true,
31369         /**
31370          * @event previewrendered
31371          * Fire when preview rendered
31372          * @param {Roo.bootstrap.DocumentManager} this
31373          * @param {Object} file
31374          */
31375         "previewrendered" : true,
31376         /**
31377          */
31378         "previewResize" : true
31379         
31380     });
31381 };
31382
31383 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31384     
31385     boxes : 0,
31386     inputName : '',
31387     thumbSize : 300,
31388     multiple : true,
31389     files : false,
31390     method : 'POST',
31391     url : '',
31392     paramName : 'imageUpload',
31393     toolTipName : 'filename',
31394     fieldLabel : '',
31395     labelWidth : 4,
31396     labelAlign : 'left',
31397     editable : true,
31398     delegates : false,
31399     xhr : false, 
31400     
31401     labellg : 0,
31402     labelmd : 0,
31403     labelsm : 0,
31404     labelxs : 0,
31405     
31406     getAutoCreate : function()
31407     {   
31408         var managerWidget = {
31409             tag : 'div',
31410             cls : 'roo-document-manager',
31411             cn : [
31412                 {
31413                     tag : 'input',
31414                     cls : 'roo-document-manager-selector',
31415                     type : 'file'
31416                 },
31417                 {
31418                     tag : 'div',
31419                     cls : 'roo-document-manager-uploader',
31420                     cn : [
31421                         {
31422                             tag : 'div',
31423                             cls : 'roo-document-manager-upload-btn',
31424                             html : '<i class="fa fa-plus"></i>'
31425                         }
31426                     ]
31427                     
31428                 }
31429             ]
31430         };
31431         
31432         var content = [
31433             {
31434                 tag : 'div',
31435                 cls : 'column col-md-12',
31436                 cn : managerWidget
31437             }
31438         ];
31439         
31440         if(this.fieldLabel.length){
31441             
31442             content = [
31443                 {
31444                     tag : 'div',
31445                     cls : 'column col-md-12',
31446                     html : this.fieldLabel
31447                 },
31448                 {
31449                     tag : 'div',
31450                     cls : 'column col-md-12',
31451                     cn : managerWidget
31452                 }
31453             ];
31454
31455             if(this.labelAlign == 'left'){
31456                 content = [
31457                     {
31458                         tag : 'div',
31459                         cls : 'column',
31460                         html : this.fieldLabel
31461                     },
31462                     {
31463                         tag : 'div',
31464                         cls : 'column',
31465                         cn : managerWidget
31466                     }
31467                 ];
31468                 
31469                 if(this.labelWidth > 12){
31470                     content[0].style = "width: " + this.labelWidth + 'px';
31471                 }
31472
31473                 if(this.labelWidth < 13 && this.labelmd == 0){
31474                     this.labelmd = this.labelWidth;
31475                 }
31476
31477                 if(this.labellg > 0){
31478                     content[0].cls += ' col-lg-' + this.labellg;
31479                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31480                 }
31481
31482                 if(this.labelmd > 0){
31483                     content[0].cls += ' col-md-' + this.labelmd;
31484                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31485                 }
31486
31487                 if(this.labelsm > 0){
31488                     content[0].cls += ' col-sm-' + this.labelsm;
31489                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31490                 }
31491
31492                 if(this.labelxs > 0){
31493                     content[0].cls += ' col-xs-' + this.labelxs;
31494                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31495                 }
31496                 
31497             }
31498         }
31499         
31500         var cfg = {
31501             tag : 'div',
31502             cls : 'row clearfix',
31503             cn : content
31504         };
31505         
31506         return cfg;
31507         
31508     },
31509     
31510     initEvents : function()
31511     {
31512         this.managerEl = this.el.select('.roo-document-manager', true).first();
31513         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31514         
31515         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31516         this.selectorEl.hide();
31517         
31518         if(this.multiple){
31519             this.selectorEl.attr('multiple', 'multiple');
31520         }
31521         
31522         this.selectorEl.on('change', this.onFileSelected, this);
31523         
31524         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31525         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31526         
31527         this.uploader.on('click', this.onUploaderClick, this);
31528         
31529         this.renderProgressDialog();
31530         
31531         var _this = this;
31532         
31533         window.addEventListener("resize", function() { _this.refresh(); } );
31534         
31535         this.fireEvent('initial', this);
31536     },
31537     
31538     renderProgressDialog : function()
31539     {
31540         var _this = this;
31541         
31542         this.progressDialog = new Roo.bootstrap.Modal({
31543             cls : 'roo-document-manager-progress-dialog',
31544             allow_close : false,
31545             animate : false,
31546             title : '',
31547             buttons : [
31548                 {
31549                     name  :'cancel',
31550                     weight : 'danger',
31551                     html : 'Cancel'
31552                 }
31553             ], 
31554             listeners : { 
31555                 btnclick : function() {
31556                     _this.uploadCancel();
31557                     this.hide();
31558                 }
31559             }
31560         });
31561          
31562         this.progressDialog.render(Roo.get(document.body));
31563          
31564         this.progress = new Roo.bootstrap.Progress({
31565             cls : 'roo-document-manager-progress',
31566             active : true,
31567             striped : true
31568         });
31569         
31570         this.progress.render(this.progressDialog.getChildContainer());
31571         
31572         this.progressBar = new Roo.bootstrap.ProgressBar({
31573             cls : 'roo-document-manager-progress-bar',
31574             aria_valuenow : 0,
31575             aria_valuemin : 0,
31576             aria_valuemax : 12,
31577             panel : 'success'
31578         });
31579         
31580         this.progressBar.render(this.progress.getChildContainer());
31581     },
31582     
31583     onUploaderClick : function(e)
31584     {
31585         e.preventDefault();
31586      
31587         if(this.fireEvent('beforeselectfile', this) != false){
31588             this.selectorEl.dom.click();
31589         }
31590         
31591     },
31592     
31593     onFileSelected : function(e)
31594     {
31595         e.preventDefault();
31596         
31597         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31598             return;
31599         }
31600         
31601         Roo.each(this.selectorEl.dom.files, function(file){
31602             if(this.fireEvent('inspect', this, file) != false){
31603                 this.files.push(file);
31604             }
31605         }, this);
31606         
31607         this.queue();
31608         
31609     },
31610     
31611     queue : function()
31612     {
31613         this.selectorEl.dom.value = '';
31614         
31615         if(!this.files || !this.files.length){
31616             return;
31617         }
31618         
31619         if(this.boxes > 0 && this.files.length > this.boxes){
31620             this.files = this.files.slice(0, this.boxes);
31621         }
31622         
31623         this.uploader.show();
31624         
31625         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31626             this.uploader.hide();
31627         }
31628         
31629         var _this = this;
31630         
31631         var files = [];
31632         
31633         var docs = [];
31634         
31635         Roo.each(this.files, function(file){
31636             
31637             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31638                 var f = this.renderPreview(file);
31639                 files.push(f);
31640                 return;
31641             }
31642             
31643             if(file.type.indexOf('image') != -1){
31644                 this.delegates.push(
31645                     (function(){
31646                         _this.process(file);
31647                     }).createDelegate(this)
31648                 );
31649         
31650                 return;
31651             }
31652             
31653             docs.push(
31654                 (function(){
31655                     _this.process(file);
31656                 }).createDelegate(this)
31657             );
31658             
31659         }, this);
31660         
31661         this.files = files;
31662         
31663         this.delegates = this.delegates.concat(docs);
31664         
31665         if(!this.delegates.length){
31666             this.refresh();
31667             return;
31668         }
31669         
31670         this.progressBar.aria_valuemax = this.delegates.length;
31671         
31672         this.arrange();
31673         
31674         return;
31675     },
31676     
31677     arrange : function()
31678     {
31679         if(!this.delegates.length){
31680             this.progressDialog.hide();
31681             this.refresh();
31682             return;
31683         }
31684         
31685         var delegate = this.delegates.shift();
31686         
31687         this.progressDialog.show();
31688         
31689         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31690         
31691         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31692         
31693         delegate();
31694     },
31695     
31696     refresh : function()
31697     {
31698         this.uploader.show();
31699         
31700         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31701             this.uploader.hide();
31702         }
31703         
31704         Roo.isTouch ? this.closable(false) : this.closable(true);
31705         
31706         this.fireEvent('refresh', this);
31707     },
31708     
31709     onRemove : function(e, el, o)
31710     {
31711         e.preventDefault();
31712         
31713         this.fireEvent('remove', this, o);
31714         
31715     },
31716     
31717     remove : function(o)
31718     {
31719         var files = [];
31720         
31721         Roo.each(this.files, function(file){
31722             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31723                 files.push(file);
31724                 return;
31725             }
31726
31727             o.target.remove();
31728
31729         }, this);
31730         
31731         this.files = files;
31732         
31733         this.refresh();
31734     },
31735     
31736     clear : function()
31737     {
31738         Roo.each(this.files, function(file){
31739             if(!file.target){
31740                 return;
31741             }
31742             
31743             file.target.remove();
31744
31745         }, this);
31746         
31747         this.files = [];
31748         
31749         this.refresh();
31750     },
31751     
31752     onClick : function(e, el, o)
31753     {
31754         e.preventDefault();
31755         
31756         this.fireEvent('click', this, o);
31757         
31758     },
31759     
31760     closable : function(closable)
31761     {
31762         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31763             
31764             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31765             
31766             if(closable){
31767                 el.show();
31768                 return;
31769             }
31770             
31771             el.hide();
31772             
31773         }, this);
31774     },
31775     
31776     xhrOnLoad : function(xhr)
31777     {
31778         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31779             el.remove();
31780         }, this);
31781         
31782         if (xhr.readyState !== 4) {
31783             this.arrange();
31784             this.fireEvent('exception', this, xhr);
31785             return;
31786         }
31787
31788         var response = Roo.decode(xhr.responseText);
31789         
31790         if(!response.success){
31791             this.arrange();
31792             this.fireEvent('exception', this, xhr);
31793             return;
31794         }
31795         
31796         var file = this.renderPreview(response.data);
31797         
31798         this.files.push(file);
31799         
31800         this.arrange();
31801         
31802         this.fireEvent('afterupload', this, xhr);
31803         
31804     },
31805     
31806     xhrOnError : function(xhr)
31807     {
31808         Roo.log('xhr on error');
31809         
31810         var response = Roo.decode(xhr.responseText);
31811           
31812         Roo.log(response);
31813         
31814         this.arrange();
31815     },
31816     
31817     process : function(file)
31818     {
31819         if(this.fireEvent('process', this, file) !== false){
31820             if(this.editable && file.type.indexOf('image') != -1){
31821                 this.fireEvent('edit', this, file);
31822                 return;
31823             }
31824
31825             this.uploadStart(file, false);
31826
31827             return;
31828         }
31829         
31830     },
31831     
31832     uploadStart : function(file, crop)
31833     {
31834         this.xhr = new XMLHttpRequest();
31835         
31836         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31837             this.arrange();
31838             return;
31839         }
31840         
31841         file.xhr = this.xhr;
31842             
31843         this.managerEl.createChild({
31844             tag : 'div',
31845             cls : 'roo-document-manager-loading',
31846             cn : [
31847                 {
31848                     tag : 'div',
31849                     tooltip : file.name,
31850                     cls : 'roo-document-manager-thumb',
31851                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31852                 }
31853             ]
31854
31855         });
31856
31857         this.xhr.open(this.method, this.url, true);
31858         
31859         var headers = {
31860             "Accept": "application/json",
31861             "Cache-Control": "no-cache",
31862             "X-Requested-With": "XMLHttpRequest"
31863         };
31864         
31865         for (var headerName in headers) {
31866             var headerValue = headers[headerName];
31867             if (headerValue) {
31868                 this.xhr.setRequestHeader(headerName, headerValue);
31869             }
31870         }
31871         
31872         var _this = this;
31873         
31874         this.xhr.onload = function()
31875         {
31876             _this.xhrOnLoad(_this.xhr);
31877         }
31878         
31879         this.xhr.onerror = function()
31880         {
31881             _this.xhrOnError(_this.xhr);
31882         }
31883         
31884         var formData = new FormData();
31885
31886         formData.append('returnHTML', 'NO');
31887         
31888         if(crop){
31889             formData.append('crop', crop);
31890         }
31891         
31892         formData.append(this.paramName, file, file.name);
31893         
31894         var options = {
31895             file : file, 
31896             manually : false
31897         };
31898         
31899         if(this.fireEvent('prepare', this, formData, options) != false){
31900             
31901             if(options.manually){
31902                 return;
31903             }
31904             
31905             this.xhr.send(formData);
31906             return;
31907         };
31908         
31909         this.uploadCancel();
31910     },
31911     
31912     uploadCancel : function()
31913     {
31914         if (this.xhr) {
31915             this.xhr.abort();
31916         }
31917         
31918         this.delegates = [];
31919         
31920         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31921             el.remove();
31922         }, this);
31923         
31924         this.arrange();
31925     },
31926     
31927     renderPreview : function(file)
31928     {
31929         if(typeof(file.target) != 'undefined' && file.target){
31930             return file;
31931         }
31932         
31933         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31934         
31935         var previewEl = this.managerEl.createChild({
31936             tag : 'div',
31937             cls : 'roo-document-manager-preview',
31938             cn : [
31939                 {
31940                     tag : 'div',
31941                     tooltip : file[this.toolTipName],
31942                     cls : 'roo-document-manager-thumb',
31943                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31944                 },
31945                 {
31946                     tag : 'button',
31947                     cls : 'close',
31948                     html : '<i class="fa fa-times-circle"></i>'
31949                 }
31950             ]
31951         });
31952
31953         var close = previewEl.select('button.close', true).first();
31954
31955         close.on('click', this.onRemove, this, file);
31956
31957         file.target = previewEl;
31958
31959         var image = previewEl.select('img', true).first();
31960         
31961         var _this = this;
31962         
31963         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31964         
31965         image.on('click', this.onClick, this, file);
31966         
31967         this.fireEvent('previewrendered', this, file);
31968         
31969         return file;
31970         
31971     },
31972     
31973     onPreviewLoad : function(file, image)
31974     {
31975         if(typeof(file.target) == 'undefined' || !file.target){
31976             return;
31977         }
31978         
31979         var width = image.dom.naturalWidth || image.dom.width;
31980         var height = image.dom.naturalHeight || image.dom.height;
31981         
31982         if(!this.previewResize) {
31983             return;
31984         }
31985         
31986         if(width > height){
31987             file.target.addClass('wide');
31988             return;
31989         }
31990         
31991         file.target.addClass('tall');
31992         return;
31993         
31994     },
31995     
31996     uploadFromSource : function(file, crop)
31997     {
31998         this.xhr = new XMLHttpRequest();
31999         
32000         this.managerEl.createChild({
32001             tag : 'div',
32002             cls : 'roo-document-manager-loading',
32003             cn : [
32004                 {
32005                     tag : 'div',
32006                     tooltip : file.name,
32007                     cls : 'roo-document-manager-thumb',
32008                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32009                 }
32010             ]
32011
32012         });
32013
32014         this.xhr.open(this.method, this.url, true);
32015         
32016         var headers = {
32017             "Accept": "application/json",
32018             "Cache-Control": "no-cache",
32019             "X-Requested-With": "XMLHttpRequest"
32020         };
32021         
32022         for (var headerName in headers) {
32023             var headerValue = headers[headerName];
32024             if (headerValue) {
32025                 this.xhr.setRequestHeader(headerName, headerValue);
32026             }
32027         }
32028         
32029         var _this = this;
32030         
32031         this.xhr.onload = function()
32032         {
32033             _this.xhrOnLoad(_this.xhr);
32034         }
32035         
32036         this.xhr.onerror = function()
32037         {
32038             _this.xhrOnError(_this.xhr);
32039         }
32040         
32041         var formData = new FormData();
32042
32043         formData.append('returnHTML', 'NO');
32044         
32045         formData.append('crop', crop);
32046         
32047         if(typeof(file.filename) != 'undefined'){
32048             formData.append('filename', file.filename);
32049         }
32050         
32051         if(typeof(file.mimetype) != 'undefined'){
32052             formData.append('mimetype', file.mimetype);
32053         }
32054         
32055         Roo.log(formData);
32056         
32057         if(this.fireEvent('prepare', this, formData) != false){
32058             this.xhr.send(formData);
32059         };
32060     }
32061 });
32062
32063 /*
32064 * Licence: LGPL
32065 */
32066
32067 /**
32068  * @class Roo.bootstrap.DocumentViewer
32069  * @extends Roo.bootstrap.Component
32070  * Bootstrap DocumentViewer class
32071  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32072  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32073  * 
32074  * @constructor
32075  * Create a new DocumentViewer
32076  * @param {Object} config The config object
32077  */
32078
32079 Roo.bootstrap.DocumentViewer = function(config){
32080     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32081     
32082     this.addEvents({
32083         /**
32084          * @event initial
32085          * Fire after initEvent
32086          * @param {Roo.bootstrap.DocumentViewer} this
32087          */
32088         "initial" : true,
32089         /**
32090          * @event click
32091          * Fire after click
32092          * @param {Roo.bootstrap.DocumentViewer} this
32093          */
32094         "click" : true,
32095         /**
32096          * @event download
32097          * Fire after download button
32098          * @param {Roo.bootstrap.DocumentViewer} this
32099          */
32100         "download" : true,
32101         /**
32102          * @event trash
32103          * Fire after trash button
32104          * @param {Roo.bootstrap.DocumentViewer} this
32105          */
32106         "trash" : true
32107         
32108     });
32109 };
32110
32111 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32112     
32113     showDownload : true,
32114     
32115     showTrash : true,
32116     
32117     getAutoCreate : function()
32118     {
32119         var cfg = {
32120             tag : 'div',
32121             cls : 'roo-document-viewer',
32122             cn : [
32123                 {
32124                     tag : 'div',
32125                     cls : 'roo-document-viewer-body',
32126                     cn : [
32127                         {
32128                             tag : 'div',
32129                             cls : 'roo-document-viewer-thumb',
32130                             cn : [
32131                                 {
32132                                     tag : 'img',
32133                                     cls : 'roo-document-viewer-image'
32134                                 }
32135                             ]
32136                         }
32137                     ]
32138                 },
32139                 {
32140                     tag : 'div',
32141                     cls : 'roo-document-viewer-footer',
32142                     cn : {
32143                         tag : 'div',
32144                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32145                         cn : [
32146                             {
32147                                 tag : 'div',
32148                                 cls : 'btn-group roo-document-viewer-download',
32149                                 cn : [
32150                                     {
32151                                         tag : 'button',
32152                                         cls : 'btn btn-default',
32153                                         html : '<i class="fa fa-download"></i>'
32154                                     }
32155                                 ]
32156                             },
32157                             {
32158                                 tag : 'div',
32159                                 cls : 'btn-group roo-document-viewer-trash',
32160                                 cn : [
32161                                     {
32162                                         tag : 'button',
32163                                         cls : 'btn btn-default',
32164                                         html : '<i class="fa fa-trash"></i>'
32165                                     }
32166                                 ]
32167                             }
32168                         ]
32169                     }
32170                 }
32171             ]
32172         };
32173         
32174         return cfg;
32175     },
32176     
32177     initEvents : function()
32178     {
32179         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32180         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32181         
32182         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32183         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32184         
32185         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32186         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32187         
32188         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32189         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32190         
32191         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32192         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32193         
32194         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32195         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32196         
32197         this.bodyEl.on('click', this.onClick, this);
32198         this.downloadBtn.on('click', this.onDownload, this);
32199         this.trashBtn.on('click', this.onTrash, this);
32200         
32201         this.downloadBtn.hide();
32202         this.trashBtn.hide();
32203         
32204         if(this.showDownload){
32205             this.downloadBtn.show();
32206         }
32207         
32208         if(this.showTrash){
32209             this.trashBtn.show();
32210         }
32211         
32212         if(!this.showDownload && !this.showTrash) {
32213             this.footerEl.hide();
32214         }
32215         
32216     },
32217     
32218     initial : function()
32219     {
32220         this.fireEvent('initial', this);
32221         
32222     },
32223     
32224     onClick : function(e)
32225     {
32226         e.preventDefault();
32227         
32228         this.fireEvent('click', this);
32229     },
32230     
32231     onDownload : function(e)
32232     {
32233         e.preventDefault();
32234         
32235         this.fireEvent('download', this);
32236     },
32237     
32238     onTrash : function(e)
32239     {
32240         e.preventDefault();
32241         
32242         this.fireEvent('trash', this);
32243     }
32244     
32245 });
32246 /*
32247  * - LGPL
32248  *
32249  * nav progress bar
32250  * 
32251  */
32252
32253 /**
32254  * @class Roo.bootstrap.NavProgressBar
32255  * @extends Roo.bootstrap.Component
32256  * Bootstrap NavProgressBar class
32257  * 
32258  * @constructor
32259  * Create a new nav progress bar
32260  * @param {Object} config The config object
32261  */
32262
32263 Roo.bootstrap.NavProgressBar = function(config){
32264     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32265
32266     this.bullets = this.bullets || [];
32267    
32268 //    Roo.bootstrap.NavProgressBar.register(this);
32269      this.addEvents({
32270         /**
32271              * @event changed
32272              * Fires when the active item changes
32273              * @param {Roo.bootstrap.NavProgressBar} this
32274              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32275              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32276          */
32277         'changed': true
32278      });
32279     
32280 };
32281
32282 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32283     
32284     bullets : [],
32285     barItems : [],
32286     
32287     getAutoCreate : function()
32288     {
32289         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32290         
32291         cfg = {
32292             tag : 'div',
32293             cls : 'roo-navigation-bar-group',
32294             cn : [
32295                 {
32296                     tag : 'div',
32297                     cls : 'roo-navigation-top-bar'
32298                 },
32299                 {
32300                     tag : 'div',
32301                     cls : 'roo-navigation-bullets-bar',
32302                     cn : [
32303                         {
32304                             tag : 'ul',
32305                             cls : 'roo-navigation-bar'
32306                         }
32307                     ]
32308                 },
32309                 
32310                 {
32311                     tag : 'div',
32312                     cls : 'roo-navigation-bottom-bar'
32313                 }
32314             ]
32315             
32316         };
32317         
32318         return cfg;
32319         
32320     },
32321     
32322     initEvents: function() 
32323     {
32324         
32325     },
32326     
32327     onRender : function(ct, position) 
32328     {
32329         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32330         
32331         if(this.bullets.length){
32332             Roo.each(this.bullets, function(b){
32333                this.addItem(b);
32334             }, this);
32335         }
32336         
32337         this.format();
32338         
32339     },
32340     
32341     addItem : function(cfg)
32342     {
32343         var item = new Roo.bootstrap.NavProgressItem(cfg);
32344         
32345         item.parentId = this.id;
32346         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32347         
32348         if(cfg.html){
32349             var top = new Roo.bootstrap.Element({
32350                 tag : 'div',
32351                 cls : 'roo-navigation-bar-text'
32352             });
32353             
32354             var bottom = new Roo.bootstrap.Element({
32355                 tag : 'div',
32356                 cls : 'roo-navigation-bar-text'
32357             });
32358             
32359             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32360             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32361             
32362             var topText = new Roo.bootstrap.Element({
32363                 tag : 'span',
32364                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32365             });
32366             
32367             var bottomText = new Roo.bootstrap.Element({
32368                 tag : 'span',
32369                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32370             });
32371             
32372             topText.onRender(top.el, null);
32373             bottomText.onRender(bottom.el, null);
32374             
32375             item.topEl = top;
32376             item.bottomEl = bottom;
32377         }
32378         
32379         this.barItems.push(item);
32380         
32381         return item;
32382     },
32383     
32384     getActive : function()
32385     {
32386         var active = false;
32387         
32388         Roo.each(this.barItems, function(v){
32389             
32390             if (!v.isActive()) {
32391                 return;
32392             }
32393             
32394             active = v;
32395             return false;
32396             
32397         });
32398         
32399         return active;
32400     },
32401     
32402     setActiveItem : function(item)
32403     {
32404         var prev = false;
32405         
32406         Roo.each(this.barItems, function(v){
32407             if (v.rid == item.rid) {
32408                 return ;
32409             }
32410             
32411             if (v.isActive()) {
32412                 v.setActive(false);
32413                 prev = v;
32414             }
32415         });
32416
32417         item.setActive(true);
32418         
32419         this.fireEvent('changed', this, item, prev);
32420     },
32421     
32422     getBarItem: function(rid)
32423     {
32424         var ret = false;
32425         
32426         Roo.each(this.barItems, function(e) {
32427             if (e.rid != rid) {
32428                 return;
32429             }
32430             
32431             ret =  e;
32432             return false;
32433         });
32434         
32435         return ret;
32436     },
32437     
32438     indexOfItem : function(item)
32439     {
32440         var index = false;
32441         
32442         Roo.each(this.barItems, function(v, i){
32443             
32444             if (v.rid != item.rid) {
32445                 return;
32446             }
32447             
32448             index = i;
32449             return false
32450         });
32451         
32452         return index;
32453     },
32454     
32455     setActiveNext : function()
32456     {
32457         var i = this.indexOfItem(this.getActive());
32458         
32459         if (i > this.barItems.length) {
32460             return;
32461         }
32462         
32463         this.setActiveItem(this.barItems[i+1]);
32464     },
32465     
32466     setActivePrev : function()
32467     {
32468         var i = this.indexOfItem(this.getActive());
32469         
32470         if (i  < 1) {
32471             return;
32472         }
32473         
32474         this.setActiveItem(this.barItems[i-1]);
32475     },
32476     
32477     format : function()
32478     {
32479         if(!this.barItems.length){
32480             return;
32481         }
32482      
32483         var width = 100 / this.barItems.length;
32484         
32485         Roo.each(this.barItems, function(i){
32486             i.el.setStyle('width', width + '%');
32487             i.topEl.el.setStyle('width', width + '%');
32488             i.bottomEl.el.setStyle('width', width + '%');
32489         }, this);
32490         
32491     }
32492     
32493 });
32494 /*
32495  * - LGPL
32496  *
32497  * Nav Progress Item
32498  * 
32499  */
32500
32501 /**
32502  * @class Roo.bootstrap.NavProgressItem
32503  * @extends Roo.bootstrap.Component
32504  * Bootstrap NavProgressItem class
32505  * @cfg {String} rid the reference id
32506  * @cfg {Boolean} active (true|false) Is item active default false
32507  * @cfg {Boolean} disabled (true|false) Is item active default false
32508  * @cfg {String} html
32509  * @cfg {String} position (top|bottom) text position default bottom
32510  * @cfg {String} icon show icon instead of number
32511  * 
32512  * @constructor
32513  * Create a new NavProgressItem
32514  * @param {Object} config The config object
32515  */
32516 Roo.bootstrap.NavProgressItem = function(config){
32517     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32518     this.addEvents({
32519         // raw events
32520         /**
32521          * @event click
32522          * The raw click event for the entire grid.
32523          * @param {Roo.bootstrap.NavProgressItem} this
32524          * @param {Roo.EventObject} e
32525          */
32526         "click" : true
32527     });
32528    
32529 };
32530
32531 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32532     
32533     rid : '',
32534     active : false,
32535     disabled : false,
32536     html : '',
32537     position : 'bottom',
32538     icon : false,
32539     
32540     getAutoCreate : function()
32541     {
32542         var iconCls = 'roo-navigation-bar-item-icon';
32543         
32544         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32545         
32546         var cfg = {
32547             tag: 'li',
32548             cls: 'roo-navigation-bar-item',
32549             cn : [
32550                 {
32551                     tag : 'i',
32552                     cls : iconCls
32553                 }
32554             ]
32555         };
32556         
32557         if(this.active){
32558             cfg.cls += ' active';
32559         }
32560         if(this.disabled){
32561             cfg.cls += ' disabled';
32562         }
32563         
32564         return cfg;
32565     },
32566     
32567     disable : function()
32568     {
32569         this.setDisabled(true);
32570     },
32571     
32572     enable : function()
32573     {
32574         this.setDisabled(false);
32575     },
32576     
32577     initEvents: function() 
32578     {
32579         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32580         
32581         this.iconEl.on('click', this.onClick, this);
32582     },
32583     
32584     onClick : function(e)
32585     {
32586         e.preventDefault();
32587         
32588         if(this.disabled){
32589             return;
32590         }
32591         
32592         if(this.fireEvent('click', this, e) === false){
32593             return;
32594         };
32595         
32596         this.parent().setActiveItem(this);
32597     },
32598     
32599     isActive: function () 
32600     {
32601         return this.active;
32602     },
32603     
32604     setActive : function(state)
32605     {
32606         if(this.active == state){
32607             return;
32608         }
32609         
32610         this.active = state;
32611         
32612         if (state) {
32613             this.el.addClass('active');
32614             return;
32615         }
32616         
32617         this.el.removeClass('active');
32618         
32619         return;
32620     },
32621     
32622     setDisabled : function(state)
32623     {
32624         if(this.disabled == state){
32625             return;
32626         }
32627         
32628         this.disabled = state;
32629         
32630         if (state) {
32631             this.el.addClass('disabled');
32632             return;
32633         }
32634         
32635         this.el.removeClass('disabled');
32636     },
32637     
32638     tooltipEl : function()
32639     {
32640         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32641     }
32642 });
32643  
32644
32645  /*
32646  * - LGPL
32647  *
32648  * FieldLabel
32649  * 
32650  */
32651
32652 /**
32653  * @class Roo.bootstrap.FieldLabel
32654  * @extends Roo.bootstrap.Component
32655  * Bootstrap FieldLabel class
32656  * @cfg {String} html contents of the element
32657  * @cfg {String} tag tag of the element default label
32658  * @cfg {String} cls class of the element
32659  * @cfg {String} target label target 
32660  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32661  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32662  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32663  * @cfg {String} iconTooltip default "This field is required"
32664  * @cfg {String} indicatorpos (left|right) default left
32665  * 
32666  * @constructor
32667  * Create a new FieldLabel
32668  * @param {Object} config The config object
32669  */
32670
32671 Roo.bootstrap.FieldLabel = function(config){
32672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32673     
32674     this.addEvents({
32675             /**
32676              * @event invalid
32677              * Fires after the field has been marked as invalid.
32678              * @param {Roo.form.FieldLabel} this
32679              * @param {String} msg The validation message
32680              */
32681             invalid : true,
32682             /**
32683              * @event valid
32684              * Fires after the field has been validated with no errors.
32685              * @param {Roo.form.FieldLabel} this
32686              */
32687             valid : true
32688         });
32689 };
32690
32691 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32692     
32693     tag: 'label',
32694     cls: '',
32695     html: '',
32696     target: '',
32697     allowBlank : true,
32698     invalidClass : 'has-warning',
32699     validClass : 'has-success',
32700     iconTooltip : 'This field is required',
32701     indicatorpos : 'left',
32702     
32703     getAutoCreate : function(){
32704         
32705         var cls = "";
32706         if (!this.allowBlank) {
32707             cls  = "visible";
32708         }
32709         
32710         var cfg = {
32711             tag : this.tag,
32712             cls : 'roo-bootstrap-field-label ' + this.cls,
32713             for : this.target,
32714             cn : [
32715                 {
32716                     tag : 'i',
32717                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32718                     tooltip : this.iconTooltip
32719                 },
32720                 {
32721                     tag : 'span',
32722                     html : this.html
32723                 }
32724             ] 
32725         };
32726         
32727         if(this.indicatorpos == 'right'){
32728             var cfg = {
32729                 tag : this.tag,
32730                 cls : 'roo-bootstrap-field-label ' + this.cls,
32731                 for : this.target,
32732                 cn : [
32733                     {
32734                         tag : 'span',
32735                         html : this.html
32736                     },
32737                     {
32738                         tag : 'i',
32739                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32740                         tooltip : this.iconTooltip
32741                     }
32742                 ] 
32743             };
32744         }
32745         
32746         return cfg;
32747     },
32748     
32749     initEvents: function() 
32750     {
32751         Roo.bootstrap.Element.superclass.initEvents.call(this);
32752         
32753         this.indicator = this.indicatorEl();
32754         
32755         if(this.indicator){
32756             this.indicator.removeClass('visible');
32757             this.indicator.addClass('invisible');
32758         }
32759         
32760         Roo.bootstrap.FieldLabel.register(this);
32761     },
32762     
32763     indicatorEl : function()
32764     {
32765         var indicator = this.el.select('i.roo-required-indicator',true).first();
32766         
32767         if(!indicator){
32768             return false;
32769         }
32770         
32771         return indicator;
32772         
32773     },
32774     
32775     /**
32776      * Mark this field as valid
32777      */
32778     markValid : function()
32779     {
32780         if(this.indicator){
32781             this.indicator.removeClass('visible');
32782             this.indicator.addClass('invisible');
32783         }
32784         if (Roo.bootstrap.version == 3) {
32785             this.el.removeClass(this.invalidClass);
32786             this.el.addClass(this.validClass);
32787         } else {
32788             this.el.removeClass('is-invalid');
32789             this.el.addClass('is-valid');
32790         }
32791         
32792         
32793         this.fireEvent('valid', this);
32794     },
32795     
32796     /**
32797      * Mark this field as invalid
32798      * @param {String} msg The validation message
32799      */
32800     markInvalid : function(msg)
32801     {
32802         if(this.indicator){
32803             this.indicator.removeClass('invisible');
32804             this.indicator.addClass('visible');
32805         }
32806           if (Roo.bootstrap.version == 3) {
32807             this.el.removeClass(this.validClass);
32808             this.el.addClass(this.invalidClass);
32809         } else {
32810             this.el.removeClass('is-valid');
32811             this.el.addClass('is-invalid');
32812         }
32813         
32814         
32815         this.fireEvent('invalid', this, msg);
32816     }
32817     
32818    
32819 });
32820
32821 Roo.apply(Roo.bootstrap.FieldLabel, {
32822     
32823     groups: {},
32824     
32825      /**
32826     * register a FieldLabel Group
32827     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32828     */
32829     register : function(label)
32830     {
32831         if(this.groups.hasOwnProperty(label.target)){
32832             return;
32833         }
32834      
32835         this.groups[label.target] = label;
32836         
32837     },
32838     /**
32839     * fetch a FieldLabel Group based on the target
32840     * @param {string} target
32841     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32842     */
32843     get: function(target) {
32844         if (typeof(this.groups[target]) == 'undefined') {
32845             return false;
32846         }
32847         
32848         return this.groups[target] ;
32849     }
32850 });
32851
32852  
32853
32854  /*
32855  * - LGPL
32856  *
32857  * page DateSplitField.
32858  * 
32859  */
32860
32861
32862 /**
32863  * @class Roo.bootstrap.DateSplitField
32864  * @extends Roo.bootstrap.Component
32865  * Bootstrap DateSplitField class
32866  * @cfg {string} fieldLabel - the label associated
32867  * @cfg {Number} labelWidth set the width of label (0-12)
32868  * @cfg {String} labelAlign (top|left)
32869  * @cfg {Boolean} dayAllowBlank (true|false) default false
32870  * @cfg {Boolean} monthAllowBlank (true|false) default false
32871  * @cfg {Boolean} yearAllowBlank (true|false) default false
32872  * @cfg {string} dayPlaceholder 
32873  * @cfg {string} monthPlaceholder
32874  * @cfg {string} yearPlaceholder
32875  * @cfg {string} dayFormat default 'd'
32876  * @cfg {string} monthFormat default 'm'
32877  * @cfg {string} yearFormat default 'Y'
32878  * @cfg {Number} labellg set the width of label (1-12)
32879  * @cfg {Number} labelmd set the width of label (1-12)
32880  * @cfg {Number} labelsm set the width of label (1-12)
32881  * @cfg {Number} labelxs set the width of label (1-12)
32882
32883  *     
32884  * @constructor
32885  * Create a new DateSplitField
32886  * @param {Object} config The config object
32887  */
32888
32889 Roo.bootstrap.DateSplitField = function(config){
32890     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32891     
32892     this.addEvents({
32893         // raw events
32894          /**
32895          * @event years
32896          * getting the data of years
32897          * @param {Roo.bootstrap.DateSplitField} this
32898          * @param {Object} years
32899          */
32900         "years" : true,
32901         /**
32902          * @event days
32903          * getting the data of days
32904          * @param {Roo.bootstrap.DateSplitField} this
32905          * @param {Object} days
32906          */
32907         "days" : true,
32908         /**
32909          * @event invalid
32910          * Fires after the field has been marked as invalid.
32911          * @param {Roo.form.Field} this
32912          * @param {String} msg The validation message
32913          */
32914         invalid : true,
32915        /**
32916          * @event valid
32917          * Fires after the field has been validated with no errors.
32918          * @param {Roo.form.Field} this
32919          */
32920         valid : true
32921     });
32922 };
32923
32924 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32925     
32926     fieldLabel : '',
32927     labelAlign : 'top',
32928     labelWidth : 3,
32929     dayAllowBlank : false,
32930     monthAllowBlank : false,
32931     yearAllowBlank : false,
32932     dayPlaceholder : '',
32933     monthPlaceholder : '',
32934     yearPlaceholder : '',
32935     dayFormat : 'd',
32936     monthFormat : 'm',
32937     yearFormat : 'Y',
32938     isFormField : true,
32939     labellg : 0,
32940     labelmd : 0,
32941     labelsm : 0,
32942     labelxs : 0,
32943     
32944     getAutoCreate : function()
32945     {
32946         var cfg = {
32947             tag : 'div',
32948             cls : 'row roo-date-split-field-group',
32949             cn : [
32950                 {
32951                     tag : 'input',
32952                     type : 'hidden',
32953                     cls : 'form-hidden-field roo-date-split-field-group-value',
32954                     name : this.name
32955                 }
32956             ]
32957         };
32958         
32959         var labelCls = 'col-md-12';
32960         var contentCls = 'col-md-4';
32961         
32962         if(this.fieldLabel){
32963             
32964             var label = {
32965                 tag : 'div',
32966                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32967                 cn : [
32968                     {
32969                         tag : 'label',
32970                         html : this.fieldLabel
32971                     }
32972                 ]
32973             };
32974             
32975             if(this.labelAlign == 'left'){
32976             
32977                 if(this.labelWidth > 12){
32978                     label.style = "width: " + this.labelWidth + 'px';
32979                 }
32980
32981                 if(this.labelWidth < 13 && this.labelmd == 0){
32982                     this.labelmd = this.labelWidth;
32983                 }
32984
32985                 if(this.labellg > 0){
32986                     labelCls = ' col-lg-' + this.labellg;
32987                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32988                 }
32989
32990                 if(this.labelmd > 0){
32991                     labelCls = ' col-md-' + this.labelmd;
32992                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32993                 }
32994
32995                 if(this.labelsm > 0){
32996                     labelCls = ' col-sm-' + this.labelsm;
32997                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32998                 }
32999
33000                 if(this.labelxs > 0){
33001                     labelCls = ' col-xs-' + this.labelxs;
33002                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33003                 }
33004             }
33005             
33006             label.cls += ' ' + labelCls;
33007             
33008             cfg.cn.push(label);
33009         }
33010         
33011         Roo.each(['day', 'month', 'year'], function(t){
33012             cfg.cn.push({
33013                 tag : 'div',
33014                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33015             });
33016         }, this);
33017         
33018         return cfg;
33019     },
33020     
33021     inputEl: function ()
33022     {
33023         return this.el.select('.roo-date-split-field-group-value', true).first();
33024     },
33025     
33026     onRender : function(ct, position) 
33027     {
33028         var _this = this;
33029         
33030         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33031         
33032         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33033         
33034         this.dayField = new Roo.bootstrap.ComboBox({
33035             allowBlank : this.dayAllowBlank,
33036             alwaysQuery : true,
33037             displayField : 'value',
33038             editable : false,
33039             fieldLabel : '',
33040             forceSelection : true,
33041             mode : 'local',
33042             placeholder : this.dayPlaceholder,
33043             selectOnFocus : true,
33044             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33045             triggerAction : 'all',
33046             typeAhead : true,
33047             valueField : 'value',
33048             store : new Roo.data.SimpleStore({
33049                 data : (function() {    
33050                     var days = [];
33051                     _this.fireEvent('days', _this, days);
33052                     return days;
33053                 })(),
33054                 fields : [ 'value' ]
33055             }),
33056             listeners : {
33057                 select : function (_self, record, index)
33058                 {
33059                     _this.setValue(_this.getValue());
33060                 }
33061             }
33062         });
33063
33064         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33065         
33066         this.monthField = new Roo.bootstrap.MonthField({
33067             after : '<i class=\"fa fa-calendar\"></i>',
33068             allowBlank : this.monthAllowBlank,
33069             placeholder : this.monthPlaceholder,
33070             readOnly : true,
33071             listeners : {
33072                 render : function (_self)
33073                 {
33074                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33075                         e.preventDefault();
33076                         _self.focus();
33077                     });
33078                 },
33079                 select : function (_self, oldvalue, newvalue)
33080                 {
33081                     _this.setValue(_this.getValue());
33082                 }
33083             }
33084         });
33085         
33086         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33087         
33088         this.yearField = new Roo.bootstrap.ComboBox({
33089             allowBlank : this.yearAllowBlank,
33090             alwaysQuery : true,
33091             displayField : 'value',
33092             editable : false,
33093             fieldLabel : '',
33094             forceSelection : true,
33095             mode : 'local',
33096             placeholder : this.yearPlaceholder,
33097             selectOnFocus : true,
33098             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33099             triggerAction : 'all',
33100             typeAhead : true,
33101             valueField : 'value',
33102             store : new Roo.data.SimpleStore({
33103                 data : (function() {
33104                     var years = [];
33105                     _this.fireEvent('years', _this, years);
33106                     return years;
33107                 })(),
33108                 fields : [ 'value' ]
33109             }),
33110             listeners : {
33111                 select : function (_self, record, index)
33112                 {
33113                     _this.setValue(_this.getValue());
33114                 }
33115             }
33116         });
33117
33118         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33119     },
33120     
33121     setValue : function(v, format)
33122     {
33123         this.inputEl.dom.value = v;
33124         
33125         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33126         
33127         var d = Date.parseDate(v, f);
33128         
33129         if(!d){
33130             this.validate();
33131             return;
33132         }
33133         
33134         this.setDay(d.format(this.dayFormat));
33135         this.setMonth(d.format(this.monthFormat));
33136         this.setYear(d.format(this.yearFormat));
33137         
33138         this.validate();
33139         
33140         return;
33141     },
33142     
33143     setDay : function(v)
33144     {
33145         this.dayField.setValue(v);
33146         this.inputEl.dom.value = this.getValue();
33147         this.validate();
33148         return;
33149     },
33150     
33151     setMonth : function(v)
33152     {
33153         this.monthField.setValue(v, true);
33154         this.inputEl.dom.value = this.getValue();
33155         this.validate();
33156         return;
33157     },
33158     
33159     setYear : function(v)
33160     {
33161         this.yearField.setValue(v);
33162         this.inputEl.dom.value = this.getValue();
33163         this.validate();
33164         return;
33165     },
33166     
33167     getDay : function()
33168     {
33169         return this.dayField.getValue();
33170     },
33171     
33172     getMonth : function()
33173     {
33174         return this.monthField.getValue();
33175     },
33176     
33177     getYear : function()
33178     {
33179         return this.yearField.getValue();
33180     },
33181     
33182     getValue : function()
33183     {
33184         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33185         
33186         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33187         
33188         return date;
33189     },
33190     
33191     reset : function()
33192     {
33193         this.setDay('');
33194         this.setMonth('');
33195         this.setYear('');
33196         this.inputEl.dom.value = '';
33197         this.validate();
33198         return;
33199     },
33200     
33201     validate : function()
33202     {
33203         var d = this.dayField.validate();
33204         var m = this.monthField.validate();
33205         var y = this.yearField.validate();
33206         
33207         var valid = true;
33208         
33209         if(
33210                 (!this.dayAllowBlank && !d) ||
33211                 (!this.monthAllowBlank && !m) ||
33212                 (!this.yearAllowBlank && !y)
33213         ){
33214             valid = false;
33215         }
33216         
33217         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33218             return valid;
33219         }
33220         
33221         if(valid){
33222             this.markValid();
33223             return valid;
33224         }
33225         
33226         this.markInvalid();
33227         
33228         return valid;
33229     },
33230     
33231     markValid : function()
33232     {
33233         
33234         var label = this.el.select('label', true).first();
33235         var icon = this.el.select('i.fa-star', true).first();
33236
33237         if(label && icon){
33238             icon.remove();
33239         }
33240         
33241         this.fireEvent('valid', this);
33242     },
33243     
33244      /**
33245      * Mark this field as invalid
33246      * @param {String} msg The validation message
33247      */
33248     markInvalid : function(msg)
33249     {
33250         
33251         var label = this.el.select('label', true).first();
33252         var icon = this.el.select('i.fa-star', true).first();
33253
33254         if(label && !icon){
33255             this.el.select('.roo-date-split-field-label', true).createChild({
33256                 tag : 'i',
33257                 cls : 'text-danger fa fa-lg fa-star',
33258                 tooltip : 'This field is required',
33259                 style : 'margin-right:5px;'
33260             }, label, true);
33261         }
33262         
33263         this.fireEvent('invalid', this, msg);
33264     },
33265     
33266     clearInvalid : function()
33267     {
33268         var label = this.el.select('label', true).first();
33269         var icon = this.el.select('i.fa-star', true).first();
33270
33271         if(label && icon){
33272             icon.remove();
33273         }
33274         
33275         this.fireEvent('valid', this);
33276     },
33277     
33278     getName: function()
33279     {
33280         return this.name;
33281     }
33282     
33283 });
33284
33285  /**
33286  *
33287  * This is based on 
33288  * http://masonry.desandro.com
33289  *
33290  * The idea is to render all the bricks based on vertical width...
33291  *
33292  * The original code extends 'outlayer' - we might need to use that....
33293  * 
33294  */
33295
33296
33297 /**
33298  * @class Roo.bootstrap.LayoutMasonry
33299  * @extends Roo.bootstrap.Component
33300  * Bootstrap Layout Masonry class
33301  * 
33302  * @constructor
33303  * Create a new Element
33304  * @param {Object} config The config object
33305  */
33306
33307 Roo.bootstrap.LayoutMasonry = function(config){
33308     
33309     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33310     
33311     this.bricks = [];
33312     
33313     Roo.bootstrap.LayoutMasonry.register(this);
33314     
33315     this.addEvents({
33316         // raw events
33317         /**
33318          * @event layout
33319          * Fire after layout the items
33320          * @param {Roo.bootstrap.LayoutMasonry} this
33321          * @param {Roo.EventObject} e
33322          */
33323         "layout" : true
33324     });
33325     
33326 };
33327
33328 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33329     
33330     /**
33331      * @cfg {Boolean} isLayoutInstant = no animation?
33332      */   
33333     isLayoutInstant : false, // needed?
33334    
33335     /**
33336      * @cfg {Number} boxWidth  width of the columns
33337      */   
33338     boxWidth : 450,
33339     
33340       /**
33341      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33342      */   
33343     boxHeight : 0,
33344     
33345     /**
33346      * @cfg {Number} padWidth padding below box..
33347      */   
33348     padWidth : 10, 
33349     
33350     /**
33351      * @cfg {Number} gutter gutter width..
33352      */   
33353     gutter : 10,
33354     
33355      /**
33356      * @cfg {Number} maxCols maximum number of columns
33357      */   
33358     
33359     maxCols: 0,
33360     
33361     /**
33362      * @cfg {Boolean} isAutoInitial defalut true
33363      */   
33364     isAutoInitial : true, 
33365     
33366     containerWidth: 0,
33367     
33368     /**
33369      * @cfg {Boolean} isHorizontal defalut false
33370      */   
33371     isHorizontal : false, 
33372
33373     currentSize : null,
33374     
33375     tag: 'div',
33376     
33377     cls: '',
33378     
33379     bricks: null, //CompositeElement
33380     
33381     cols : 1,
33382     
33383     _isLayoutInited : false,
33384     
33385 //    isAlternative : false, // only use for vertical layout...
33386     
33387     /**
33388      * @cfg {Number} alternativePadWidth padding below box..
33389      */   
33390     alternativePadWidth : 50,
33391     
33392     selectedBrick : [],
33393     
33394     getAutoCreate : function(){
33395         
33396         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33397         
33398         var cfg = {
33399             tag: this.tag,
33400             cls: 'blog-masonary-wrapper ' + this.cls,
33401             cn : {
33402                 cls : 'mas-boxes masonary'
33403             }
33404         };
33405         
33406         return cfg;
33407     },
33408     
33409     getChildContainer: function( )
33410     {
33411         if (this.boxesEl) {
33412             return this.boxesEl;
33413         }
33414         
33415         this.boxesEl = this.el.select('.mas-boxes').first();
33416         
33417         return this.boxesEl;
33418     },
33419     
33420     
33421     initEvents : function()
33422     {
33423         var _this = this;
33424         
33425         if(this.isAutoInitial){
33426             Roo.log('hook children rendered');
33427             this.on('childrenrendered', function() {
33428                 Roo.log('children rendered');
33429                 _this.initial();
33430             } ,this);
33431         }
33432     },
33433     
33434     initial : function()
33435     {
33436         this.selectedBrick = [];
33437         
33438         this.currentSize = this.el.getBox(true);
33439         
33440         Roo.EventManager.onWindowResize(this.resize, this); 
33441
33442         if(!this.isAutoInitial){
33443             this.layout();
33444             return;
33445         }
33446         
33447         this.layout();
33448         
33449         return;
33450         //this.layout.defer(500,this);
33451         
33452     },
33453     
33454     resize : function()
33455     {
33456         var cs = this.el.getBox(true);
33457         
33458         if (
33459                 this.currentSize.width == cs.width && 
33460                 this.currentSize.x == cs.x && 
33461                 this.currentSize.height == cs.height && 
33462                 this.currentSize.y == cs.y 
33463         ) {
33464             Roo.log("no change in with or X or Y");
33465             return;
33466         }
33467         
33468         this.currentSize = cs;
33469         
33470         this.layout();
33471         
33472     },
33473     
33474     layout : function()
33475     {   
33476         this._resetLayout();
33477         
33478         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33479         
33480         this.layoutItems( isInstant );
33481       
33482         this._isLayoutInited = true;
33483         
33484         this.fireEvent('layout', this);
33485         
33486     },
33487     
33488     _resetLayout : function()
33489     {
33490         if(this.isHorizontal){
33491             this.horizontalMeasureColumns();
33492             return;
33493         }
33494         
33495         this.verticalMeasureColumns();
33496         
33497     },
33498     
33499     verticalMeasureColumns : function()
33500     {
33501         this.getContainerWidth();
33502         
33503 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33504 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33505 //            return;
33506 //        }
33507         
33508         var boxWidth = this.boxWidth + this.padWidth;
33509         
33510         if(this.containerWidth < this.boxWidth){
33511             boxWidth = this.containerWidth
33512         }
33513         
33514         var containerWidth = this.containerWidth;
33515         
33516         var cols = Math.floor(containerWidth / boxWidth);
33517         
33518         this.cols = Math.max( cols, 1 );
33519         
33520         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33521         
33522         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33523         
33524         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33525         
33526         this.colWidth = boxWidth + avail - this.padWidth;
33527         
33528         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33529         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33530     },
33531     
33532     horizontalMeasureColumns : function()
33533     {
33534         this.getContainerWidth();
33535         
33536         var boxWidth = this.boxWidth;
33537         
33538         if(this.containerWidth < boxWidth){
33539             boxWidth = this.containerWidth;
33540         }
33541         
33542         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33543         
33544         this.el.setHeight(boxWidth);
33545         
33546     },
33547     
33548     getContainerWidth : function()
33549     {
33550         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33551     },
33552     
33553     layoutItems : function( isInstant )
33554     {
33555         Roo.log(this.bricks);
33556         
33557         var items = Roo.apply([], this.bricks);
33558         
33559         if(this.isHorizontal){
33560             this._horizontalLayoutItems( items , isInstant );
33561             return;
33562         }
33563         
33564 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33565 //            this._verticalAlternativeLayoutItems( items , isInstant );
33566 //            return;
33567 //        }
33568         
33569         this._verticalLayoutItems( items , isInstant );
33570         
33571     },
33572     
33573     _verticalLayoutItems : function ( items , isInstant)
33574     {
33575         if ( !items || !items.length ) {
33576             return;
33577         }
33578         
33579         var standard = [
33580             ['xs', 'xs', 'xs', 'tall'],
33581             ['xs', 'xs', 'tall'],
33582             ['xs', 'xs', 'sm'],
33583             ['xs', 'xs', 'xs'],
33584             ['xs', 'tall'],
33585             ['xs', 'sm'],
33586             ['xs', 'xs'],
33587             ['xs'],
33588             
33589             ['sm', 'xs', 'xs'],
33590             ['sm', 'xs'],
33591             ['sm'],
33592             
33593             ['tall', 'xs', 'xs', 'xs'],
33594             ['tall', 'xs', 'xs'],
33595             ['tall', 'xs'],
33596             ['tall']
33597             
33598         ];
33599         
33600         var queue = [];
33601         
33602         var boxes = [];
33603         
33604         var box = [];
33605         
33606         Roo.each(items, function(item, k){
33607             
33608             switch (item.size) {
33609                 // these layouts take up a full box,
33610                 case 'md' :
33611                 case 'md-left' :
33612                 case 'md-right' :
33613                 case 'wide' :
33614                     
33615                     if(box.length){
33616                         boxes.push(box);
33617                         box = [];
33618                     }
33619                     
33620                     boxes.push([item]);
33621                     
33622                     break;
33623                     
33624                 case 'xs' :
33625                 case 'sm' :
33626                 case 'tall' :
33627                     
33628                     box.push(item);
33629                     
33630                     break;
33631                 default :
33632                     break;
33633                     
33634             }
33635             
33636         }, this);
33637         
33638         if(box.length){
33639             boxes.push(box);
33640             box = [];
33641         }
33642         
33643         var filterPattern = function(box, length)
33644         {
33645             if(!box.length){
33646                 return;
33647             }
33648             
33649             var match = false;
33650             
33651             var pattern = box.slice(0, length);
33652             
33653             var format = [];
33654             
33655             Roo.each(pattern, function(i){
33656                 format.push(i.size);
33657             }, this);
33658             
33659             Roo.each(standard, function(s){
33660                 
33661                 if(String(s) != String(format)){
33662                     return;
33663                 }
33664                 
33665                 match = true;
33666                 return false;
33667                 
33668             }, this);
33669             
33670             if(!match && length == 1){
33671                 return;
33672             }
33673             
33674             if(!match){
33675                 filterPattern(box, length - 1);
33676                 return;
33677             }
33678                 
33679             queue.push(pattern);
33680
33681             box = box.slice(length, box.length);
33682
33683             filterPattern(box, 4);
33684
33685             return;
33686             
33687         }
33688         
33689         Roo.each(boxes, function(box, k){
33690             
33691             if(!box.length){
33692                 return;
33693             }
33694             
33695             if(box.length == 1){
33696                 queue.push(box);
33697                 return;
33698             }
33699             
33700             filterPattern(box, 4);
33701             
33702         }, this);
33703         
33704         this._processVerticalLayoutQueue( queue, isInstant );
33705         
33706     },
33707     
33708 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33709 //    {
33710 //        if ( !items || !items.length ) {
33711 //            return;
33712 //        }
33713 //
33714 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33715 //        
33716 //    },
33717     
33718     _horizontalLayoutItems : function ( items , isInstant)
33719     {
33720         if ( !items || !items.length || items.length < 3) {
33721             return;
33722         }
33723         
33724         items.reverse();
33725         
33726         var eItems = items.slice(0, 3);
33727         
33728         items = items.slice(3, items.length);
33729         
33730         var standard = [
33731             ['xs', 'xs', 'xs', 'wide'],
33732             ['xs', 'xs', 'wide'],
33733             ['xs', 'xs', 'sm'],
33734             ['xs', 'xs', 'xs'],
33735             ['xs', 'wide'],
33736             ['xs', 'sm'],
33737             ['xs', 'xs'],
33738             ['xs'],
33739             
33740             ['sm', 'xs', 'xs'],
33741             ['sm', 'xs'],
33742             ['sm'],
33743             
33744             ['wide', 'xs', 'xs', 'xs'],
33745             ['wide', 'xs', 'xs'],
33746             ['wide', 'xs'],
33747             ['wide'],
33748             
33749             ['wide-thin']
33750         ];
33751         
33752         var queue = [];
33753         
33754         var boxes = [];
33755         
33756         var box = [];
33757         
33758         Roo.each(items, function(item, k){
33759             
33760             switch (item.size) {
33761                 case 'md' :
33762                 case 'md-left' :
33763                 case 'md-right' :
33764                 case 'tall' :
33765                     
33766                     if(box.length){
33767                         boxes.push(box);
33768                         box = [];
33769                     }
33770                     
33771                     boxes.push([item]);
33772                     
33773                     break;
33774                     
33775                 case 'xs' :
33776                 case 'sm' :
33777                 case 'wide' :
33778                 case 'wide-thin' :
33779                     
33780                     box.push(item);
33781                     
33782                     break;
33783                 default :
33784                     break;
33785                     
33786             }
33787             
33788         }, this);
33789         
33790         if(box.length){
33791             boxes.push(box);
33792             box = [];
33793         }
33794         
33795         var filterPattern = function(box, length)
33796         {
33797             if(!box.length){
33798                 return;
33799             }
33800             
33801             var match = false;
33802             
33803             var pattern = box.slice(0, length);
33804             
33805             var format = [];
33806             
33807             Roo.each(pattern, function(i){
33808                 format.push(i.size);
33809             }, this);
33810             
33811             Roo.each(standard, function(s){
33812                 
33813                 if(String(s) != String(format)){
33814                     return;
33815                 }
33816                 
33817                 match = true;
33818                 return false;
33819                 
33820             }, this);
33821             
33822             if(!match && length == 1){
33823                 return;
33824             }
33825             
33826             if(!match){
33827                 filterPattern(box, length - 1);
33828                 return;
33829             }
33830                 
33831             queue.push(pattern);
33832
33833             box = box.slice(length, box.length);
33834
33835             filterPattern(box, 4);
33836
33837             return;
33838             
33839         }
33840         
33841         Roo.each(boxes, function(box, k){
33842             
33843             if(!box.length){
33844                 return;
33845             }
33846             
33847             if(box.length == 1){
33848                 queue.push(box);
33849                 return;
33850             }
33851             
33852             filterPattern(box, 4);
33853             
33854         }, this);
33855         
33856         
33857         var prune = [];
33858         
33859         var pos = this.el.getBox(true);
33860         
33861         var minX = pos.x;
33862         
33863         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33864         
33865         var hit_end = false;
33866         
33867         Roo.each(queue, function(box){
33868             
33869             if(hit_end){
33870                 
33871                 Roo.each(box, function(b){
33872                 
33873                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33874                     b.el.hide();
33875
33876                 }, this);
33877
33878                 return;
33879             }
33880             
33881             var mx = 0;
33882             
33883             Roo.each(box, function(b){
33884                 
33885                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33886                 b.el.show();
33887
33888                 mx = Math.max(mx, b.x);
33889                 
33890             }, this);
33891             
33892             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33893             
33894             if(maxX < minX){
33895                 
33896                 Roo.each(box, function(b){
33897                 
33898                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33899                     b.el.hide();
33900                     
33901                 }, this);
33902                 
33903                 hit_end = true;
33904                 
33905                 return;
33906             }
33907             
33908             prune.push(box);
33909             
33910         }, this);
33911         
33912         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33913     },
33914     
33915     /** Sets position of item in DOM
33916     * @param {Element} item
33917     * @param {Number} x - horizontal position
33918     * @param {Number} y - vertical position
33919     * @param {Boolean} isInstant - disables transitions
33920     */
33921     _processVerticalLayoutQueue : function( queue, isInstant )
33922     {
33923         var pos = this.el.getBox(true);
33924         var x = pos.x;
33925         var y = pos.y;
33926         var maxY = [];
33927         
33928         for (var i = 0; i < this.cols; i++){
33929             maxY[i] = pos.y;
33930         }
33931         
33932         Roo.each(queue, function(box, k){
33933             
33934             var col = k % this.cols;
33935             
33936             Roo.each(box, function(b,kk){
33937                 
33938                 b.el.position('absolute');
33939                 
33940                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33941                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33942                 
33943                 if(b.size == 'md-left' || b.size == 'md-right'){
33944                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33945                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33946                 }
33947                 
33948                 b.el.setWidth(width);
33949                 b.el.setHeight(height);
33950                 // iframe?
33951                 b.el.select('iframe',true).setSize(width,height);
33952                 
33953             }, this);
33954             
33955             for (var i = 0; i < this.cols; i++){
33956                 
33957                 if(maxY[i] < maxY[col]){
33958                     col = i;
33959                     continue;
33960                 }
33961                 
33962                 col = Math.min(col, i);
33963                 
33964             }
33965             
33966             x = pos.x + col * (this.colWidth + this.padWidth);
33967             
33968             y = maxY[col];
33969             
33970             var positions = [];
33971             
33972             switch (box.length){
33973                 case 1 :
33974                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33975                     break;
33976                 case 2 :
33977                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33978                     break;
33979                 case 3 :
33980                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33981                     break;
33982                 case 4 :
33983                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33984                     break;
33985                 default :
33986                     break;
33987             }
33988             
33989             Roo.each(box, function(b,kk){
33990                 
33991                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33992                 
33993                 var sz = b.el.getSize();
33994                 
33995                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33996                 
33997             }, this);
33998             
33999         }, this);
34000         
34001         var mY = 0;
34002         
34003         for (var i = 0; i < this.cols; i++){
34004             mY = Math.max(mY, maxY[i]);
34005         }
34006         
34007         this.el.setHeight(mY - pos.y);
34008         
34009     },
34010     
34011 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34012 //    {
34013 //        var pos = this.el.getBox(true);
34014 //        var x = pos.x;
34015 //        var y = pos.y;
34016 //        var maxX = pos.right;
34017 //        
34018 //        var maxHeight = 0;
34019 //        
34020 //        Roo.each(items, function(item, k){
34021 //            
34022 //            var c = k % 2;
34023 //            
34024 //            item.el.position('absolute');
34025 //                
34026 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34027 //
34028 //            item.el.setWidth(width);
34029 //
34030 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34031 //
34032 //            item.el.setHeight(height);
34033 //            
34034 //            if(c == 0){
34035 //                item.el.setXY([x, y], isInstant ? false : true);
34036 //            } else {
34037 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34038 //            }
34039 //            
34040 //            y = y + height + this.alternativePadWidth;
34041 //            
34042 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34043 //            
34044 //        }, this);
34045 //        
34046 //        this.el.setHeight(maxHeight);
34047 //        
34048 //    },
34049     
34050     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34051     {
34052         var pos = this.el.getBox(true);
34053         
34054         var minX = pos.x;
34055         var minY = pos.y;
34056         
34057         var maxX = pos.right;
34058         
34059         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34060         
34061         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34062         
34063         Roo.each(queue, function(box, k){
34064             
34065             Roo.each(box, function(b, kk){
34066                 
34067                 b.el.position('absolute');
34068                 
34069                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34070                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34071                 
34072                 if(b.size == 'md-left' || b.size == 'md-right'){
34073                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34074                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34075                 }
34076                 
34077                 b.el.setWidth(width);
34078                 b.el.setHeight(height);
34079                 
34080             }, this);
34081             
34082             if(!box.length){
34083                 return;
34084             }
34085             
34086             var positions = [];
34087             
34088             switch (box.length){
34089                 case 1 :
34090                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34091                     break;
34092                 case 2 :
34093                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34094                     break;
34095                 case 3 :
34096                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34097                     break;
34098                 case 4 :
34099                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34100                     break;
34101                 default :
34102                     break;
34103             }
34104             
34105             Roo.each(box, function(b,kk){
34106                 
34107                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34108                 
34109                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34110                 
34111             }, this);
34112             
34113         }, this);
34114         
34115     },
34116     
34117     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34118     {
34119         Roo.each(eItems, function(b,k){
34120             
34121             b.size = (k == 0) ? 'sm' : 'xs';
34122             b.x = (k == 0) ? 2 : 1;
34123             b.y = (k == 0) ? 2 : 1;
34124             
34125             b.el.position('absolute');
34126             
34127             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34128                 
34129             b.el.setWidth(width);
34130             
34131             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34132             
34133             b.el.setHeight(height);
34134             
34135         }, this);
34136
34137         var positions = [];
34138         
34139         positions.push({
34140             x : maxX - this.unitWidth * 2 - this.gutter,
34141             y : minY
34142         });
34143         
34144         positions.push({
34145             x : maxX - this.unitWidth,
34146             y : minY + (this.unitWidth + this.gutter) * 2
34147         });
34148         
34149         positions.push({
34150             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34151             y : minY
34152         });
34153         
34154         Roo.each(eItems, function(b,k){
34155             
34156             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34157
34158         }, this);
34159         
34160     },
34161     
34162     getVerticalOneBoxColPositions : function(x, y, box)
34163     {
34164         var pos = [];
34165         
34166         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34167         
34168         if(box[0].size == 'md-left'){
34169             rand = 0;
34170         }
34171         
34172         if(box[0].size == 'md-right'){
34173             rand = 1;
34174         }
34175         
34176         pos.push({
34177             x : x + (this.unitWidth + this.gutter) * rand,
34178             y : y
34179         });
34180         
34181         return pos;
34182     },
34183     
34184     getVerticalTwoBoxColPositions : function(x, y, box)
34185     {
34186         var pos = [];
34187         
34188         if(box[0].size == 'xs'){
34189             
34190             pos.push({
34191                 x : x,
34192                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34193             });
34194
34195             pos.push({
34196                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34197                 y : y
34198             });
34199             
34200             return pos;
34201             
34202         }
34203         
34204         pos.push({
34205             x : x,
34206             y : y
34207         });
34208
34209         pos.push({
34210             x : x + (this.unitWidth + this.gutter) * 2,
34211             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34212         });
34213         
34214         return pos;
34215         
34216     },
34217     
34218     getVerticalThreeBoxColPositions : function(x, y, box)
34219     {
34220         var pos = [];
34221         
34222         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34223             
34224             pos.push({
34225                 x : x,
34226                 y : y
34227             });
34228
34229             pos.push({
34230                 x : x + (this.unitWidth + this.gutter) * 1,
34231                 y : y
34232             });
34233             
34234             pos.push({
34235                 x : x + (this.unitWidth + this.gutter) * 2,
34236                 y : y
34237             });
34238             
34239             return pos;
34240             
34241         }
34242         
34243         if(box[0].size == 'xs' && box[1].size == 'xs'){
34244             
34245             pos.push({
34246                 x : x,
34247                 y : y
34248             });
34249
34250             pos.push({
34251                 x : x,
34252                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34253             });
34254             
34255             pos.push({
34256                 x : x + (this.unitWidth + this.gutter) * 1,
34257                 y : y
34258             });
34259             
34260             return pos;
34261             
34262         }
34263         
34264         pos.push({
34265             x : x,
34266             y : y
34267         });
34268
34269         pos.push({
34270             x : x + (this.unitWidth + this.gutter) * 2,
34271             y : y
34272         });
34273
34274         pos.push({
34275             x : x + (this.unitWidth + this.gutter) * 2,
34276             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34277         });
34278             
34279         return pos;
34280         
34281     },
34282     
34283     getVerticalFourBoxColPositions : function(x, y, box)
34284     {
34285         var pos = [];
34286         
34287         if(box[0].size == 'xs'){
34288             
34289             pos.push({
34290                 x : x,
34291                 y : y
34292             });
34293
34294             pos.push({
34295                 x : x,
34296                 y : y + (this.unitHeight + this.gutter) * 1
34297             });
34298             
34299             pos.push({
34300                 x : x,
34301                 y : y + (this.unitHeight + this.gutter) * 2
34302             });
34303             
34304             pos.push({
34305                 x : x + (this.unitWidth + this.gutter) * 1,
34306                 y : y
34307             });
34308             
34309             return pos;
34310             
34311         }
34312         
34313         pos.push({
34314             x : x,
34315             y : y
34316         });
34317
34318         pos.push({
34319             x : x + (this.unitWidth + this.gutter) * 2,
34320             y : y
34321         });
34322
34323         pos.push({
34324             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34325             y : y + (this.unitHeight + this.gutter) * 1
34326         });
34327
34328         pos.push({
34329             x : x + (this.unitWidth + this.gutter) * 2,
34330             y : y + (this.unitWidth + this.gutter) * 2
34331         });
34332
34333         return pos;
34334         
34335     },
34336     
34337     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34338     {
34339         var pos = [];
34340         
34341         if(box[0].size == 'md-left'){
34342             pos.push({
34343                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34344                 y : minY
34345             });
34346             
34347             return pos;
34348         }
34349         
34350         if(box[0].size == 'md-right'){
34351             pos.push({
34352                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34353                 y : minY + (this.unitWidth + this.gutter) * 1
34354             });
34355             
34356             return pos;
34357         }
34358         
34359         var rand = Math.floor(Math.random() * (4 - box[0].y));
34360         
34361         pos.push({
34362             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34363             y : minY + (this.unitWidth + this.gutter) * rand
34364         });
34365         
34366         return pos;
34367         
34368     },
34369     
34370     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34371     {
34372         var pos = [];
34373         
34374         if(box[0].size == 'xs'){
34375             
34376             pos.push({
34377                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34378                 y : minY
34379             });
34380
34381             pos.push({
34382                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34383                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34384             });
34385             
34386             return pos;
34387             
34388         }
34389         
34390         pos.push({
34391             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34392             y : minY
34393         });
34394
34395         pos.push({
34396             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34397             y : minY + (this.unitWidth + this.gutter) * 2
34398         });
34399         
34400         return pos;
34401         
34402     },
34403     
34404     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34405     {
34406         var pos = [];
34407         
34408         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34409             
34410             pos.push({
34411                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34412                 y : minY
34413             });
34414
34415             pos.push({
34416                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34417                 y : minY + (this.unitWidth + this.gutter) * 1
34418             });
34419             
34420             pos.push({
34421                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34422                 y : minY + (this.unitWidth + this.gutter) * 2
34423             });
34424             
34425             return pos;
34426             
34427         }
34428         
34429         if(box[0].size == 'xs' && box[1].size == 'xs'){
34430             
34431             pos.push({
34432                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34433                 y : minY
34434             });
34435
34436             pos.push({
34437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34438                 y : minY
34439             });
34440             
34441             pos.push({
34442                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34443                 y : minY + (this.unitWidth + this.gutter) * 1
34444             });
34445             
34446             return pos;
34447             
34448         }
34449         
34450         pos.push({
34451             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34452             y : minY
34453         });
34454
34455         pos.push({
34456             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34457             y : minY + (this.unitWidth + this.gutter) * 2
34458         });
34459
34460         pos.push({
34461             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34462             y : minY + (this.unitWidth + this.gutter) * 2
34463         });
34464             
34465         return pos;
34466         
34467     },
34468     
34469     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34470     {
34471         var pos = [];
34472         
34473         if(box[0].size == 'xs'){
34474             
34475             pos.push({
34476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34477                 y : minY
34478             });
34479
34480             pos.push({
34481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34482                 y : minY
34483             });
34484             
34485             pos.push({
34486                 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),
34487                 y : minY
34488             });
34489             
34490             pos.push({
34491                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34492                 y : minY + (this.unitWidth + this.gutter) * 1
34493             });
34494             
34495             return pos;
34496             
34497         }
34498         
34499         pos.push({
34500             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34501             y : minY
34502         });
34503         
34504         pos.push({
34505             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34506             y : minY + (this.unitWidth + this.gutter) * 2
34507         });
34508         
34509         pos.push({
34510             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34511             y : minY + (this.unitWidth + this.gutter) * 2
34512         });
34513         
34514         pos.push({
34515             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),
34516             y : minY + (this.unitWidth + this.gutter) * 2
34517         });
34518
34519         return pos;
34520         
34521     },
34522     
34523     /**
34524     * remove a Masonry Brick
34525     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34526     */
34527     removeBrick : function(brick_id)
34528     {
34529         if (!brick_id) {
34530             return;
34531         }
34532         
34533         for (var i = 0; i<this.bricks.length; i++) {
34534             if (this.bricks[i].id == brick_id) {
34535                 this.bricks.splice(i,1);
34536                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34537                 this.initial();
34538             }
34539         }
34540     },
34541     
34542     /**
34543     * adds a Masonry Brick
34544     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34545     */
34546     addBrick : function(cfg)
34547     {
34548         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34549         //this.register(cn);
34550         cn.parentId = this.id;
34551         cn.render(this.el);
34552         return cn;
34553     },
34554     
34555     /**
34556     * register a Masonry Brick
34557     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34558     */
34559     
34560     register : function(brick)
34561     {
34562         this.bricks.push(brick);
34563         brick.masonryId = this.id;
34564     },
34565     
34566     /**
34567     * clear all the Masonry Brick
34568     */
34569     clearAll : function()
34570     {
34571         this.bricks = [];
34572         //this.getChildContainer().dom.innerHTML = "";
34573         this.el.dom.innerHTML = '';
34574     },
34575     
34576     getSelected : function()
34577     {
34578         if (!this.selectedBrick) {
34579             return false;
34580         }
34581         
34582         return this.selectedBrick;
34583     }
34584 });
34585
34586 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34587     
34588     groups: {},
34589      /**
34590     * register a Masonry Layout
34591     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34592     */
34593     
34594     register : function(layout)
34595     {
34596         this.groups[layout.id] = layout;
34597     },
34598     /**
34599     * fetch a  Masonry Layout based on the masonry layout ID
34600     * @param {string} the masonry layout to add
34601     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34602     */
34603     
34604     get: function(layout_id) {
34605         if (typeof(this.groups[layout_id]) == 'undefined') {
34606             return false;
34607         }
34608         return this.groups[layout_id] ;
34609     }
34610     
34611     
34612     
34613 });
34614
34615  
34616
34617  /**
34618  *
34619  * This is based on 
34620  * http://masonry.desandro.com
34621  *
34622  * The idea is to render all the bricks based on vertical width...
34623  *
34624  * The original code extends 'outlayer' - we might need to use that....
34625  * 
34626  */
34627
34628
34629 /**
34630  * @class Roo.bootstrap.LayoutMasonryAuto
34631  * @extends Roo.bootstrap.Component
34632  * Bootstrap Layout Masonry class
34633  * 
34634  * @constructor
34635  * Create a new Element
34636  * @param {Object} config The config object
34637  */
34638
34639 Roo.bootstrap.LayoutMasonryAuto = function(config){
34640     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34641 };
34642
34643 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34644     
34645       /**
34646      * @cfg {Boolean} isFitWidth  - resize the width..
34647      */   
34648     isFitWidth : false,  // options..
34649     /**
34650      * @cfg {Boolean} isOriginLeft = left align?
34651      */   
34652     isOriginLeft : true,
34653     /**
34654      * @cfg {Boolean} isOriginTop = top align?
34655      */   
34656     isOriginTop : false,
34657     /**
34658      * @cfg {Boolean} isLayoutInstant = no animation?
34659      */   
34660     isLayoutInstant : false, // needed?
34661     /**
34662      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34663      */   
34664     isResizingContainer : true,
34665     /**
34666      * @cfg {Number} columnWidth  width of the columns 
34667      */   
34668     
34669     columnWidth : 0,
34670     
34671     /**
34672      * @cfg {Number} maxCols maximum number of columns
34673      */   
34674     
34675     maxCols: 0,
34676     /**
34677      * @cfg {Number} padHeight padding below box..
34678      */   
34679     
34680     padHeight : 10, 
34681     
34682     /**
34683      * @cfg {Boolean} isAutoInitial defalut true
34684      */   
34685     
34686     isAutoInitial : true, 
34687     
34688     // private?
34689     gutter : 0,
34690     
34691     containerWidth: 0,
34692     initialColumnWidth : 0,
34693     currentSize : null,
34694     
34695     colYs : null, // array.
34696     maxY : 0,
34697     padWidth: 10,
34698     
34699     
34700     tag: 'div',
34701     cls: '',
34702     bricks: null, //CompositeElement
34703     cols : 0, // array?
34704     // element : null, // wrapped now this.el
34705     _isLayoutInited : null, 
34706     
34707     
34708     getAutoCreate : function(){
34709         
34710         var cfg = {
34711             tag: this.tag,
34712             cls: 'blog-masonary-wrapper ' + this.cls,
34713             cn : {
34714                 cls : 'mas-boxes masonary'
34715             }
34716         };
34717         
34718         return cfg;
34719     },
34720     
34721     getChildContainer: function( )
34722     {
34723         if (this.boxesEl) {
34724             return this.boxesEl;
34725         }
34726         
34727         this.boxesEl = this.el.select('.mas-boxes').first();
34728         
34729         return this.boxesEl;
34730     },
34731     
34732     
34733     initEvents : function()
34734     {
34735         var _this = this;
34736         
34737         if(this.isAutoInitial){
34738             Roo.log('hook children rendered');
34739             this.on('childrenrendered', function() {
34740                 Roo.log('children rendered');
34741                 _this.initial();
34742             } ,this);
34743         }
34744         
34745     },
34746     
34747     initial : function()
34748     {
34749         this.reloadItems();
34750
34751         this.currentSize = this.el.getBox(true);
34752
34753         /// was window resize... - let's see if this works..
34754         Roo.EventManager.onWindowResize(this.resize, this); 
34755
34756         if(!this.isAutoInitial){
34757             this.layout();
34758             return;
34759         }
34760         
34761         this.layout.defer(500,this);
34762     },
34763     
34764     reloadItems: function()
34765     {
34766         this.bricks = this.el.select('.masonry-brick', true);
34767         
34768         this.bricks.each(function(b) {
34769             //Roo.log(b.getSize());
34770             if (!b.attr('originalwidth')) {
34771                 b.attr('originalwidth',  b.getSize().width);
34772             }
34773             
34774         });
34775         
34776         Roo.log(this.bricks.elements.length);
34777     },
34778     
34779     resize : function()
34780     {
34781         Roo.log('resize');
34782         var cs = this.el.getBox(true);
34783         
34784         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34785             Roo.log("no change in with or X");
34786             return;
34787         }
34788         this.currentSize = cs;
34789         this.layout();
34790     },
34791     
34792     layout : function()
34793     {
34794          Roo.log('layout');
34795         this._resetLayout();
34796         //this._manageStamps();
34797       
34798         // don't animate first layout
34799         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34800         this.layoutItems( isInstant );
34801       
34802         // flag for initalized
34803         this._isLayoutInited = true;
34804     },
34805     
34806     layoutItems : function( isInstant )
34807     {
34808         //var items = this._getItemsForLayout( this.items );
34809         // original code supports filtering layout items.. we just ignore it..
34810         
34811         this._layoutItems( this.bricks , isInstant );
34812       
34813         this._postLayout();
34814     },
34815     _layoutItems : function ( items , isInstant)
34816     {
34817        //this.fireEvent( 'layout', this, items );
34818     
34819
34820         if ( !items || !items.elements.length ) {
34821           // no items, emit event with empty array
34822             return;
34823         }
34824
34825         var queue = [];
34826         items.each(function(item) {
34827             Roo.log("layout item");
34828             Roo.log(item);
34829             // get x/y object from method
34830             var position = this._getItemLayoutPosition( item );
34831             // enqueue
34832             position.item = item;
34833             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34834             queue.push( position );
34835         }, this);
34836       
34837         this._processLayoutQueue( queue );
34838     },
34839     /** Sets position of item in DOM
34840     * @param {Element} item
34841     * @param {Number} x - horizontal position
34842     * @param {Number} y - vertical position
34843     * @param {Boolean} isInstant - disables transitions
34844     */
34845     _processLayoutQueue : function( queue )
34846     {
34847         for ( var i=0, len = queue.length; i < len; i++ ) {
34848             var obj = queue[i];
34849             obj.item.position('absolute');
34850             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34851         }
34852     },
34853       
34854     
34855     /**
34856     * Any logic you want to do after each layout,
34857     * i.e. size the container
34858     */
34859     _postLayout : function()
34860     {
34861         this.resizeContainer();
34862     },
34863     
34864     resizeContainer : function()
34865     {
34866         if ( !this.isResizingContainer ) {
34867             return;
34868         }
34869         var size = this._getContainerSize();
34870         if ( size ) {
34871             this.el.setSize(size.width,size.height);
34872             this.boxesEl.setSize(size.width,size.height);
34873         }
34874     },
34875     
34876     
34877     
34878     _resetLayout : function()
34879     {
34880         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34881         this.colWidth = this.el.getWidth();
34882         //this.gutter = this.el.getWidth(); 
34883         
34884         this.measureColumns();
34885
34886         // reset column Y
34887         var i = this.cols;
34888         this.colYs = [];
34889         while (i--) {
34890             this.colYs.push( 0 );
34891         }
34892     
34893         this.maxY = 0;
34894     },
34895
34896     measureColumns : function()
34897     {
34898         this.getContainerWidth();
34899       // if columnWidth is 0, default to outerWidth of first item
34900         if ( !this.columnWidth ) {
34901             var firstItem = this.bricks.first();
34902             Roo.log(firstItem);
34903             this.columnWidth  = this.containerWidth;
34904             if (firstItem && firstItem.attr('originalwidth') ) {
34905                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34906             }
34907             // columnWidth fall back to item of first element
34908             Roo.log("set column width?");
34909                         this.initialColumnWidth = this.columnWidth  ;
34910
34911             // if first elem has no width, default to size of container
34912             
34913         }
34914         
34915         
34916         if (this.initialColumnWidth) {
34917             this.columnWidth = this.initialColumnWidth;
34918         }
34919         
34920         
34921             
34922         // column width is fixed at the top - however if container width get's smaller we should
34923         // reduce it...
34924         
34925         // this bit calcs how man columns..
34926             
34927         var columnWidth = this.columnWidth += this.gutter;
34928       
34929         // calculate columns
34930         var containerWidth = this.containerWidth + this.gutter;
34931         
34932         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34933         // fix rounding errors, typically with gutters
34934         var excess = columnWidth - containerWidth % columnWidth;
34935         
34936         
34937         // if overshoot is less than a pixel, round up, otherwise floor it
34938         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34939         cols = Math[ mathMethod ]( cols );
34940         this.cols = Math.max( cols, 1 );
34941         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34942         
34943          // padding positioning..
34944         var totalColWidth = this.cols * this.columnWidth;
34945         var padavail = this.containerWidth - totalColWidth;
34946         // so for 2 columns - we need 3 'pads'
34947         
34948         var padNeeded = (1+this.cols) * this.padWidth;
34949         
34950         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34951         
34952         this.columnWidth += padExtra
34953         //this.padWidth = Math.floor(padavail /  ( this.cols));
34954         
34955         // adjust colum width so that padding is fixed??
34956         
34957         // we have 3 columns ... total = width * 3
34958         // we have X left over... that should be used by 
34959         
34960         //if (this.expandC) {
34961             
34962         //}
34963         
34964         
34965         
34966     },
34967     
34968     getContainerWidth : function()
34969     {
34970        /* // container is parent if fit width
34971         var container = this.isFitWidth ? this.element.parentNode : this.element;
34972         // check that this.size and size are there
34973         // IE8 triggers resize on body size change, so they might not be
34974         
34975         var size = getSize( container );  //FIXME
34976         this.containerWidth = size && size.innerWidth; //FIXME
34977         */
34978          
34979         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34980         
34981     },
34982     
34983     _getItemLayoutPosition : function( item )  // what is item?
34984     {
34985         // we resize the item to our columnWidth..
34986       
34987         item.setWidth(this.columnWidth);
34988         item.autoBoxAdjust  = false;
34989         
34990         var sz = item.getSize();
34991  
34992         // how many columns does this brick span
34993         var remainder = this.containerWidth % this.columnWidth;
34994         
34995         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34996         // round if off by 1 pixel, otherwise use ceil
34997         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34998         colSpan = Math.min( colSpan, this.cols );
34999         
35000         // normally this should be '1' as we dont' currently allow multi width columns..
35001         
35002         var colGroup = this._getColGroup( colSpan );
35003         // get the minimum Y value from the columns
35004         var minimumY = Math.min.apply( Math, colGroup );
35005         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35006         
35007         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35008          
35009         // position the brick
35010         var position = {
35011             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35012             y: this.currentSize.y + minimumY + this.padHeight
35013         };
35014         
35015         Roo.log(position);
35016         // apply setHeight to necessary columns
35017         var setHeight = minimumY + sz.height + this.padHeight;
35018         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35019         
35020         var setSpan = this.cols + 1 - colGroup.length;
35021         for ( var i = 0; i < setSpan; i++ ) {
35022           this.colYs[ shortColIndex + i ] = setHeight ;
35023         }
35024       
35025         return position;
35026     },
35027     
35028     /**
35029      * @param {Number} colSpan - number of columns the element spans
35030      * @returns {Array} colGroup
35031      */
35032     _getColGroup : function( colSpan )
35033     {
35034         if ( colSpan < 2 ) {
35035           // if brick spans only one column, use all the column Ys
35036           return this.colYs;
35037         }
35038       
35039         var colGroup = [];
35040         // how many different places could this brick fit horizontally
35041         var groupCount = this.cols + 1 - colSpan;
35042         // for each group potential horizontal position
35043         for ( var i = 0; i < groupCount; i++ ) {
35044           // make an array of colY values for that one group
35045           var groupColYs = this.colYs.slice( i, i + colSpan );
35046           // and get the max value of the array
35047           colGroup[i] = Math.max.apply( Math, groupColYs );
35048         }
35049         return colGroup;
35050     },
35051     /*
35052     _manageStamp : function( stamp )
35053     {
35054         var stampSize =  stamp.getSize();
35055         var offset = stamp.getBox();
35056         // get the columns that this stamp affects
35057         var firstX = this.isOriginLeft ? offset.x : offset.right;
35058         var lastX = firstX + stampSize.width;
35059         var firstCol = Math.floor( firstX / this.columnWidth );
35060         firstCol = Math.max( 0, firstCol );
35061         
35062         var lastCol = Math.floor( lastX / this.columnWidth );
35063         // lastCol should not go over if multiple of columnWidth #425
35064         lastCol -= lastX % this.columnWidth ? 0 : 1;
35065         lastCol = Math.min( this.cols - 1, lastCol );
35066         
35067         // set colYs to bottom of the stamp
35068         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35069             stampSize.height;
35070             
35071         for ( var i = firstCol; i <= lastCol; i++ ) {
35072           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35073         }
35074     },
35075     */
35076     
35077     _getContainerSize : function()
35078     {
35079         this.maxY = Math.max.apply( Math, this.colYs );
35080         var size = {
35081             height: this.maxY
35082         };
35083       
35084         if ( this.isFitWidth ) {
35085             size.width = this._getContainerFitWidth();
35086         }
35087       
35088         return size;
35089     },
35090     
35091     _getContainerFitWidth : function()
35092     {
35093         var unusedCols = 0;
35094         // count unused columns
35095         var i = this.cols;
35096         while ( --i ) {
35097           if ( this.colYs[i] !== 0 ) {
35098             break;
35099           }
35100           unusedCols++;
35101         }
35102         // fit container to columns that have been used
35103         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35104     },
35105     
35106     needsResizeLayout : function()
35107     {
35108         var previousWidth = this.containerWidth;
35109         this.getContainerWidth();
35110         return previousWidth !== this.containerWidth;
35111     }
35112  
35113 });
35114
35115  
35116
35117  /*
35118  * - LGPL
35119  *
35120  * element
35121  * 
35122  */
35123
35124 /**
35125  * @class Roo.bootstrap.MasonryBrick
35126  * @extends Roo.bootstrap.Component
35127  * Bootstrap MasonryBrick class
35128  * 
35129  * @constructor
35130  * Create a new MasonryBrick
35131  * @param {Object} config The config object
35132  */
35133
35134 Roo.bootstrap.MasonryBrick = function(config){
35135     
35136     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35137     
35138     Roo.bootstrap.MasonryBrick.register(this);
35139     
35140     this.addEvents({
35141         // raw events
35142         /**
35143          * @event click
35144          * When a MasonryBrick is clcik
35145          * @param {Roo.bootstrap.MasonryBrick} this
35146          * @param {Roo.EventObject} e
35147          */
35148         "click" : true
35149     });
35150 };
35151
35152 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35153     
35154     /**
35155      * @cfg {String} title
35156      */   
35157     title : '',
35158     /**
35159      * @cfg {String} html
35160      */   
35161     html : '',
35162     /**
35163      * @cfg {String} bgimage
35164      */   
35165     bgimage : '',
35166     /**
35167      * @cfg {String} videourl
35168      */   
35169     videourl : '',
35170     /**
35171      * @cfg {String} cls
35172      */   
35173     cls : '',
35174     /**
35175      * @cfg {String} href
35176      */   
35177     href : '',
35178     /**
35179      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35180      */   
35181     size : 'xs',
35182     
35183     /**
35184      * @cfg {String} placetitle (center|bottom)
35185      */   
35186     placetitle : '',
35187     
35188     /**
35189      * @cfg {Boolean} isFitContainer defalut true
35190      */   
35191     isFitContainer : true, 
35192     
35193     /**
35194      * @cfg {Boolean} preventDefault defalut false
35195      */   
35196     preventDefault : false, 
35197     
35198     /**
35199      * @cfg {Boolean} inverse defalut false
35200      */   
35201     maskInverse : false, 
35202     
35203     getAutoCreate : function()
35204     {
35205         if(!this.isFitContainer){
35206             return this.getSplitAutoCreate();
35207         }
35208         
35209         var cls = 'masonry-brick masonry-brick-full';
35210         
35211         if(this.href.length){
35212             cls += ' masonry-brick-link';
35213         }
35214         
35215         if(this.bgimage.length){
35216             cls += ' masonry-brick-image';
35217         }
35218         
35219         if(this.maskInverse){
35220             cls += ' mask-inverse';
35221         }
35222         
35223         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35224             cls += ' enable-mask';
35225         }
35226         
35227         if(this.size){
35228             cls += ' masonry-' + this.size + '-brick';
35229         }
35230         
35231         if(this.placetitle.length){
35232             
35233             switch (this.placetitle) {
35234                 case 'center' :
35235                     cls += ' masonry-center-title';
35236                     break;
35237                 case 'bottom' :
35238                     cls += ' masonry-bottom-title';
35239                     break;
35240                 default:
35241                     break;
35242             }
35243             
35244         } else {
35245             if(!this.html.length && !this.bgimage.length){
35246                 cls += ' masonry-center-title';
35247             }
35248
35249             if(!this.html.length && this.bgimage.length){
35250                 cls += ' masonry-bottom-title';
35251             }
35252         }
35253         
35254         if(this.cls){
35255             cls += ' ' + this.cls;
35256         }
35257         
35258         var cfg = {
35259             tag: (this.href.length) ? 'a' : 'div',
35260             cls: cls,
35261             cn: [
35262                 {
35263                     tag: 'div',
35264                     cls: 'masonry-brick-mask'
35265                 },
35266                 {
35267                     tag: 'div',
35268                     cls: 'masonry-brick-paragraph',
35269                     cn: []
35270                 }
35271             ]
35272         };
35273         
35274         if(this.href.length){
35275             cfg.href = this.href;
35276         }
35277         
35278         var cn = cfg.cn[1].cn;
35279         
35280         if(this.title.length){
35281             cn.push({
35282                 tag: 'h4',
35283                 cls: 'masonry-brick-title',
35284                 html: this.title
35285             });
35286         }
35287         
35288         if(this.html.length){
35289             cn.push({
35290                 tag: 'p',
35291                 cls: 'masonry-brick-text',
35292                 html: this.html
35293             });
35294         }
35295         
35296         if (!this.title.length && !this.html.length) {
35297             cfg.cn[1].cls += ' hide';
35298         }
35299         
35300         if(this.bgimage.length){
35301             cfg.cn.push({
35302                 tag: 'img',
35303                 cls: 'masonry-brick-image-view',
35304                 src: this.bgimage
35305             });
35306         }
35307         
35308         if(this.videourl.length){
35309             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35310             // youtube support only?
35311             cfg.cn.push({
35312                 tag: 'iframe',
35313                 cls: 'masonry-brick-image-view',
35314                 src: vurl,
35315                 frameborder : 0,
35316                 allowfullscreen : true
35317             });
35318         }
35319         
35320         return cfg;
35321         
35322     },
35323     
35324     getSplitAutoCreate : function()
35325     {
35326         var cls = 'masonry-brick masonry-brick-split';
35327         
35328         if(this.href.length){
35329             cls += ' masonry-brick-link';
35330         }
35331         
35332         if(this.bgimage.length){
35333             cls += ' masonry-brick-image';
35334         }
35335         
35336         if(this.size){
35337             cls += ' masonry-' + this.size + '-brick';
35338         }
35339         
35340         switch (this.placetitle) {
35341             case 'center' :
35342                 cls += ' masonry-center-title';
35343                 break;
35344             case 'bottom' :
35345                 cls += ' masonry-bottom-title';
35346                 break;
35347             default:
35348                 if(!this.bgimage.length){
35349                     cls += ' masonry-center-title';
35350                 }
35351
35352                 if(this.bgimage.length){
35353                     cls += ' masonry-bottom-title';
35354                 }
35355                 break;
35356         }
35357         
35358         if(this.cls){
35359             cls += ' ' + this.cls;
35360         }
35361         
35362         var cfg = {
35363             tag: (this.href.length) ? 'a' : 'div',
35364             cls: cls,
35365             cn: [
35366                 {
35367                     tag: 'div',
35368                     cls: 'masonry-brick-split-head',
35369                     cn: [
35370                         {
35371                             tag: 'div',
35372                             cls: 'masonry-brick-paragraph',
35373                             cn: []
35374                         }
35375                     ]
35376                 },
35377                 {
35378                     tag: 'div',
35379                     cls: 'masonry-brick-split-body',
35380                     cn: []
35381                 }
35382             ]
35383         };
35384         
35385         if(this.href.length){
35386             cfg.href = this.href;
35387         }
35388         
35389         if(this.title.length){
35390             cfg.cn[0].cn[0].cn.push({
35391                 tag: 'h4',
35392                 cls: 'masonry-brick-title',
35393                 html: this.title
35394             });
35395         }
35396         
35397         if(this.html.length){
35398             cfg.cn[1].cn.push({
35399                 tag: 'p',
35400                 cls: 'masonry-brick-text',
35401                 html: this.html
35402             });
35403         }
35404
35405         if(this.bgimage.length){
35406             cfg.cn[0].cn.push({
35407                 tag: 'img',
35408                 cls: 'masonry-brick-image-view',
35409                 src: this.bgimage
35410             });
35411         }
35412         
35413         if(this.videourl.length){
35414             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35415             // youtube support only?
35416             cfg.cn[0].cn.cn.push({
35417                 tag: 'iframe',
35418                 cls: 'masonry-brick-image-view',
35419                 src: vurl,
35420                 frameborder : 0,
35421                 allowfullscreen : true
35422             });
35423         }
35424         
35425         return cfg;
35426     },
35427     
35428     initEvents: function() 
35429     {
35430         switch (this.size) {
35431             case 'xs' :
35432                 this.x = 1;
35433                 this.y = 1;
35434                 break;
35435             case 'sm' :
35436                 this.x = 2;
35437                 this.y = 2;
35438                 break;
35439             case 'md' :
35440             case 'md-left' :
35441             case 'md-right' :
35442                 this.x = 3;
35443                 this.y = 3;
35444                 break;
35445             case 'tall' :
35446                 this.x = 2;
35447                 this.y = 3;
35448                 break;
35449             case 'wide' :
35450                 this.x = 3;
35451                 this.y = 2;
35452                 break;
35453             case 'wide-thin' :
35454                 this.x = 3;
35455                 this.y = 1;
35456                 break;
35457                         
35458             default :
35459                 break;
35460         }
35461         
35462         if(Roo.isTouch){
35463             this.el.on('touchstart', this.onTouchStart, this);
35464             this.el.on('touchmove', this.onTouchMove, this);
35465             this.el.on('touchend', this.onTouchEnd, this);
35466             this.el.on('contextmenu', this.onContextMenu, this);
35467         } else {
35468             this.el.on('mouseenter'  ,this.enter, this);
35469             this.el.on('mouseleave', this.leave, this);
35470             this.el.on('click', this.onClick, this);
35471         }
35472         
35473         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35474             this.parent().bricks.push(this);   
35475         }
35476         
35477     },
35478     
35479     onClick: function(e, el)
35480     {
35481         var time = this.endTimer - this.startTimer;
35482         // Roo.log(e.preventDefault());
35483         if(Roo.isTouch){
35484             if(time > 1000){
35485                 e.preventDefault();
35486                 return;
35487             }
35488         }
35489         
35490         if(!this.preventDefault){
35491             return;
35492         }
35493         
35494         e.preventDefault();
35495         
35496         if (this.activeClass != '') {
35497             this.selectBrick();
35498         }
35499         
35500         this.fireEvent('click', this, e);
35501     },
35502     
35503     enter: function(e, el)
35504     {
35505         e.preventDefault();
35506         
35507         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35508             return;
35509         }
35510         
35511         if(this.bgimage.length && this.html.length){
35512             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35513         }
35514     },
35515     
35516     leave: function(e, el)
35517     {
35518         e.preventDefault();
35519         
35520         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35521             return;
35522         }
35523         
35524         if(this.bgimage.length && this.html.length){
35525             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35526         }
35527     },
35528     
35529     onTouchStart: function(e, el)
35530     {
35531 //        e.preventDefault();
35532         
35533         this.touchmoved = false;
35534         
35535         if(!this.isFitContainer){
35536             return;
35537         }
35538         
35539         if(!this.bgimage.length || !this.html.length){
35540             return;
35541         }
35542         
35543         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35544         
35545         this.timer = new Date().getTime();
35546         
35547     },
35548     
35549     onTouchMove: function(e, el)
35550     {
35551         this.touchmoved = true;
35552     },
35553     
35554     onContextMenu : function(e,el)
35555     {
35556         e.preventDefault();
35557         e.stopPropagation();
35558         return false;
35559     },
35560     
35561     onTouchEnd: function(e, el)
35562     {
35563 //        e.preventDefault();
35564         
35565         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35566         
35567             this.leave(e,el);
35568             
35569             return;
35570         }
35571         
35572         if(!this.bgimage.length || !this.html.length){
35573             
35574             if(this.href.length){
35575                 window.location.href = this.href;
35576             }
35577             
35578             return;
35579         }
35580         
35581         if(!this.isFitContainer){
35582             return;
35583         }
35584         
35585         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35586         
35587         window.location.href = this.href;
35588     },
35589     
35590     //selection on single brick only
35591     selectBrick : function() {
35592         
35593         if (!this.parentId) {
35594             return;
35595         }
35596         
35597         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35598         var index = m.selectedBrick.indexOf(this.id);
35599         
35600         if ( index > -1) {
35601             m.selectedBrick.splice(index,1);
35602             this.el.removeClass(this.activeClass);
35603             return;
35604         }
35605         
35606         for(var i = 0; i < m.selectedBrick.length; i++) {
35607             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35608             b.el.removeClass(b.activeClass);
35609         }
35610         
35611         m.selectedBrick = [];
35612         
35613         m.selectedBrick.push(this.id);
35614         this.el.addClass(this.activeClass);
35615         return;
35616     },
35617     
35618     isSelected : function(){
35619         return this.el.hasClass(this.activeClass);
35620         
35621     }
35622 });
35623
35624 Roo.apply(Roo.bootstrap.MasonryBrick, {
35625     
35626     //groups: {},
35627     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35628      /**
35629     * register a Masonry Brick
35630     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35631     */
35632     
35633     register : function(brick)
35634     {
35635         //this.groups[brick.id] = brick;
35636         this.groups.add(brick.id, brick);
35637     },
35638     /**
35639     * fetch a  masonry brick based on the masonry brick ID
35640     * @param {string} the masonry brick to add
35641     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35642     */
35643     
35644     get: function(brick_id) 
35645     {
35646         // if (typeof(this.groups[brick_id]) == 'undefined') {
35647         //     return false;
35648         // }
35649         // return this.groups[brick_id] ;
35650         
35651         if(this.groups.key(brick_id)) {
35652             return this.groups.key(brick_id);
35653         }
35654         
35655         return false;
35656     }
35657     
35658     
35659     
35660 });
35661
35662  /*
35663  * - LGPL
35664  *
35665  * element
35666  * 
35667  */
35668
35669 /**
35670  * @class Roo.bootstrap.Brick
35671  * @extends Roo.bootstrap.Component
35672  * Bootstrap Brick class
35673  * 
35674  * @constructor
35675  * Create a new Brick
35676  * @param {Object} config The config object
35677  */
35678
35679 Roo.bootstrap.Brick = function(config){
35680     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35681     
35682     this.addEvents({
35683         // raw events
35684         /**
35685          * @event click
35686          * When a Brick is click
35687          * @param {Roo.bootstrap.Brick} this
35688          * @param {Roo.EventObject} e
35689          */
35690         "click" : true
35691     });
35692 };
35693
35694 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35695     
35696     /**
35697      * @cfg {String} title
35698      */   
35699     title : '',
35700     /**
35701      * @cfg {String} html
35702      */   
35703     html : '',
35704     /**
35705      * @cfg {String} bgimage
35706      */   
35707     bgimage : '',
35708     /**
35709      * @cfg {String} cls
35710      */   
35711     cls : '',
35712     /**
35713      * @cfg {String} href
35714      */   
35715     href : '',
35716     /**
35717      * @cfg {String} video
35718      */   
35719     video : '',
35720     /**
35721      * @cfg {Boolean} square
35722      */   
35723     square : true,
35724     
35725     getAutoCreate : function()
35726     {
35727         var cls = 'roo-brick';
35728         
35729         if(this.href.length){
35730             cls += ' roo-brick-link';
35731         }
35732         
35733         if(this.bgimage.length){
35734             cls += ' roo-brick-image';
35735         }
35736         
35737         if(!this.html.length && !this.bgimage.length){
35738             cls += ' roo-brick-center-title';
35739         }
35740         
35741         if(!this.html.length && this.bgimage.length){
35742             cls += ' roo-brick-bottom-title';
35743         }
35744         
35745         if(this.cls){
35746             cls += ' ' + this.cls;
35747         }
35748         
35749         var cfg = {
35750             tag: (this.href.length) ? 'a' : 'div',
35751             cls: cls,
35752             cn: [
35753                 {
35754                     tag: 'div',
35755                     cls: 'roo-brick-paragraph',
35756                     cn: []
35757                 }
35758             ]
35759         };
35760         
35761         if(this.href.length){
35762             cfg.href = this.href;
35763         }
35764         
35765         var cn = cfg.cn[0].cn;
35766         
35767         if(this.title.length){
35768             cn.push({
35769                 tag: 'h4',
35770                 cls: 'roo-brick-title',
35771                 html: this.title
35772             });
35773         }
35774         
35775         if(this.html.length){
35776             cn.push({
35777                 tag: 'p',
35778                 cls: 'roo-brick-text',
35779                 html: this.html
35780             });
35781         } else {
35782             cn.cls += ' hide';
35783         }
35784         
35785         if(this.bgimage.length){
35786             cfg.cn.push({
35787                 tag: 'img',
35788                 cls: 'roo-brick-image-view',
35789                 src: this.bgimage
35790             });
35791         }
35792         
35793         return cfg;
35794     },
35795     
35796     initEvents: function() 
35797     {
35798         if(this.title.length || this.html.length){
35799             this.el.on('mouseenter'  ,this.enter, this);
35800             this.el.on('mouseleave', this.leave, this);
35801         }
35802         
35803         Roo.EventManager.onWindowResize(this.resize, this); 
35804         
35805         if(this.bgimage.length){
35806             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35807             this.imageEl.on('load', this.onImageLoad, this);
35808             return;
35809         }
35810         
35811         this.resize();
35812     },
35813     
35814     onImageLoad : function()
35815     {
35816         this.resize();
35817     },
35818     
35819     resize : function()
35820     {
35821         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35822         
35823         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35824         
35825         if(this.bgimage.length){
35826             var image = this.el.select('.roo-brick-image-view', true).first();
35827             
35828             image.setWidth(paragraph.getWidth());
35829             
35830             if(this.square){
35831                 image.setHeight(paragraph.getWidth());
35832             }
35833             
35834             this.el.setHeight(image.getHeight());
35835             paragraph.setHeight(image.getHeight());
35836             
35837         }
35838         
35839     },
35840     
35841     enter: function(e, el)
35842     {
35843         e.preventDefault();
35844         
35845         if(this.bgimage.length){
35846             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35847             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35848         }
35849     },
35850     
35851     leave: function(e, el)
35852     {
35853         e.preventDefault();
35854         
35855         if(this.bgimage.length){
35856             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35857             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35858         }
35859     }
35860     
35861 });
35862
35863  
35864
35865  /*
35866  * - LGPL
35867  *
35868  * Number field 
35869  */
35870
35871 /**
35872  * @class Roo.bootstrap.NumberField
35873  * @extends Roo.bootstrap.Input
35874  * Bootstrap NumberField class
35875  * 
35876  * 
35877  * 
35878  * 
35879  * @constructor
35880  * Create a new NumberField
35881  * @param {Object} config The config object
35882  */
35883
35884 Roo.bootstrap.NumberField = function(config){
35885     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35886 };
35887
35888 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35889     
35890     /**
35891      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35892      */
35893     allowDecimals : true,
35894     /**
35895      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35896      */
35897     decimalSeparator : ".",
35898     /**
35899      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35900      */
35901     decimalPrecision : 2,
35902     /**
35903      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35904      */
35905     allowNegative : true,
35906     
35907     /**
35908      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35909      */
35910     allowZero: true,
35911     /**
35912      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35913      */
35914     minValue : Number.NEGATIVE_INFINITY,
35915     /**
35916      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35917      */
35918     maxValue : Number.MAX_VALUE,
35919     /**
35920      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35921      */
35922     minText : "The minimum value for this field is {0}",
35923     /**
35924      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35925      */
35926     maxText : "The maximum value for this field is {0}",
35927     /**
35928      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35929      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35930      */
35931     nanText : "{0} is not a valid number",
35932     /**
35933      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35934      */
35935     thousandsDelimiter : false,
35936     /**
35937      * @cfg {String} valueAlign alignment of value
35938      */
35939     valueAlign : "left",
35940
35941     getAutoCreate : function()
35942     {
35943         var hiddenInput = {
35944             tag: 'input',
35945             type: 'hidden',
35946             id: Roo.id(),
35947             cls: 'hidden-number-input'
35948         };
35949         
35950         if (this.name) {
35951             hiddenInput.name = this.name;
35952         }
35953         
35954         this.name = '';
35955         
35956         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35957         
35958         this.name = hiddenInput.name;
35959         
35960         if(cfg.cn.length > 0) {
35961             cfg.cn.push(hiddenInput);
35962         }
35963         
35964         return cfg;
35965     },
35966
35967     // private
35968     initEvents : function()
35969     {   
35970         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35971         
35972         var allowed = "0123456789";
35973         
35974         if(this.allowDecimals){
35975             allowed += this.decimalSeparator;
35976         }
35977         
35978         if(this.allowNegative){
35979             allowed += "-";
35980         }
35981         
35982         if(this.thousandsDelimiter) {
35983             allowed += ",";
35984         }
35985         
35986         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35987         
35988         var keyPress = function(e){
35989             
35990             var k = e.getKey();
35991             
35992             var c = e.getCharCode();
35993             
35994             if(
35995                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35996                     allowed.indexOf(String.fromCharCode(c)) === -1
35997             ){
35998                 e.stopEvent();
35999                 return;
36000             }
36001             
36002             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36003                 return;
36004             }
36005             
36006             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36007                 e.stopEvent();
36008             }
36009         };
36010         
36011         this.el.on("keypress", keyPress, this);
36012     },
36013     
36014     validateValue : function(value)
36015     {
36016         
36017         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36018             return false;
36019         }
36020         
36021         var num = this.parseValue(value);
36022         
36023         if(isNaN(num)){
36024             this.markInvalid(String.format(this.nanText, value));
36025             return false;
36026         }
36027         
36028         if(num < this.minValue){
36029             this.markInvalid(String.format(this.minText, this.minValue));
36030             return false;
36031         }
36032         
36033         if(num > this.maxValue){
36034             this.markInvalid(String.format(this.maxText, this.maxValue));
36035             return false;
36036         }
36037         
36038         return true;
36039     },
36040
36041     getValue : function()
36042     {
36043         var v = this.hiddenEl().getValue();
36044         
36045         return this.fixPrecision(this.parseValue(v));
36046     },
36047
36048     parseValue : function(value)
36049     {
36050         if(this.thousandsDelimiter) {
36051             value += "";
36052             r = new RegExp(",", "g");
36053             value = value.replace(r, "");
36054         }
36055         
36056         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36057         return isNaN(value) ? '' : value;
36058     },
36059
36060     fixPrecision : function(value)
36061     {
36062         if(this.thousandsDelimiter) {
36063             value += "";
36064             r = new RegExp(",", "g");
36065             value = value.replace(r, "");
36066         }
36067         
36068         var nan = isNaN(value);
36069         
36070         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36071             return nan ? '' : value;
36072         }
36073         return parseFloat(value).toFixed(this.decimalPrecision);
36074     },
36075
36076     setValue : function(v)
36077     {
36078         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36079         
36080         this.value = v;
36081         
36082         if(this.rendered){
36083             
36084             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36085             
36086             this.inputEl().dom.value = (v == '') ? '' :
36087                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36088             
36089             if(!this.allowZero && v === '0') {
36090                 this.hiddenEl().dom.value = '';
36091                 this.inputEl().dom.value = '';
36092             }
36093             
36094             this.validate();
36095         }
36096     },
36097
36098     decimalPrecisionFcn : function(v)
36099     {
36100         return Math.floor(v);
36101     },
36102
36103     beforeBlur : function()
36104     {
36105         var v = this.parseValue(this.getRawValue());
36106         
36107         if(v || v === 0 || v === ''){
36108             this.setValue(v);
36109         }
36110     },
36111     
36112     hiddenEl : function()
36113     {
36114         return this.el.select('input.hidden-number-input',true).first();
36115     }
36116     
36117 });
36118
36119  
36120
36121 /*
36122 * Licence: LGPL
36123 */
36124
36125 /**
36126  * @class Roo.bootstrap.DocumentSlider
36127  * @extends Roo.bootstrap.Component
36128  * Bootstrap DocumentSlider class
36129  * 
36130  * @constructor
36131  * Create a new DocumentViewer
36132  * @param {Object} config The config object
36133  */
36134
36135 Roo.bootstrap.DocumentSlider = function(config){
36136     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36137     
36138     this.files = [];
36139     
36140     this.addEvents({
36141         /**
36142          * @event initial
36143          * Fire after initEvent
36144          * @param {Roo.bootstrap.DocumentSlider} this
36145          */
36146         "initial" : true,
36147         /**
36148          * @event update
36149          * Fire after update
36150          * @param {Roo.bootstrap.DocumentSlider} this
36151          */
36152         "update" : true,
36153         /**
36154          * @event click
36155          * Fire after click
36156          * @param {Roo.bootstrap.DocumentSlider} this
36157          */
36158         "click" : true
36159     });
36160 };
36161
36162 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36163     
36164     files : false,
36165     
36166     indicator : 0,
36167     
36168     getAutoCreate : function()
36169     {
36170         var cfg = {
36171             tag : 'div',
36172             cls : 'roo-document-slider',
36173             cn : [
36174                 {
36175                     tag : 'div',
36176                     cls : 'roo-document-slider-header',
36177                     cn : [
36178                         {
36179                             tag : 'div',
36180                             cls : 'roo-document-slider-header-title'
36181                         }
36182                     ]
36183                 },
36184                 {
36185                     tag : 'div',
36186                     cls : 'roo-document-slider-body',
36187                     cn : [
36188                         {
36189                             tag : 'div',
36190                             cls : 'roo-document-slider-prev',
36191                             cn : [
36192                                 {
36193                                     tag : 'i',
36194                                     cls : 'fa fa-chevron-left'
36195                                 }
36196                             ]
36197                         },
36198                         {
36199                             tag : 'div',
36200                             cls : 'roo-document-slider-thumb',
36201                             cn : [
36202                                 {
36203                                     tag : 'img',
36204                                     cls : 'roo-document-slider-image'
36205                                 }
36206                             ]
36207                         },
36208                         {
36209                             tag : 'div',
36210                             cls : 'roo-document-slider-next',
36211                             cn : [
36212                                 {
36213                                     tag : 'i',
36214                                     cls : 'fa fa-chevron-right'
36215                                 }
36216                             ]
36217                         }
36218                     ]
36219                 }
36220             ]
36221         };
36222         
36223         return cfg;
36224     },
36225     
36226     initEvents : function()
36227     {
36228         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36229         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36230         
36231         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36232         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36233         
36234         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36235         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36236         
36237         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36238         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36239         
36240         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36241         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36242         
36243         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36244         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36245         
36246         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36247         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36248         
36249         this.thumbEl.on('click', this.onClick, this);
36250         
36251         this.prevIndicator.on('click', this.prev, this);
36252         
36253         this.nextIndicator.on('click', this.next, this);
36254         
36255     },
36256     
36257     initial : function()
36258     {
36259         if(this.files.length){
36260             this.indicator = 1;
36261             this.update()
36262         }
36263         
36264         this.fireEvent('initial', this);
36265     },
36266     
36267     update : function()
36268     {
36269         this.imageEl.attr('src', this.files[this.indicator - 1]);
36270         
36271         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36272         
36273         this.prevIndicator.show();
36274         
36275         if(this.indicator == 1){
36276             this.prevIndicator.hide();
36277         }
36278         
36279         this.nextIndicator.show();
36280         
36281         if(this.indicator == this.files.length){
36282             this.nextIndicator.hide();
36283         }
36284         
36285         this.thumbEl.scrollTo('top');
36286         
36287         this.fireEvent('update', this);
36288     },
36289     
36290     onClick : function(e)
36291     {
36292         e.preventDefault();
36293         
36294         this.fireEvent('click', this);
36295     },
36296     
36297     prev : function(e)
36298     {
36299         e.preventDefault();
36300         
36301         this.indicator = Math.max(1, this.indicator - 1);
36302         
36303         this.update();
36304     },
36305     
36306     next : function(e)
36307     {
36308         e.preventDefault();
36309         
36310         this.indicator = Math.min(this.files.length, this.indicator + 1);
36311         
36312         this.update();
36313     }
36314 });
36315 /*
36316  * - LGPL
36317  *
36318  * RadioSet
36319  *
36320  *
36321  */
36322
36323 /**
36324  * @class Roo.bootstrap.RadioSet
36325  * @extends Roo.bootstrap.Input
36326  * Bootstrap RadioSet class
36327  * @cfg {String} indicatorpos (left|right) default left
36328  * @cfg {Boolean} inline (true|false) inline the element (default true)
36329  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36330  * @constructor
36331  * Create a new RadioSet
36332  * @param {Object} config The config object
36333  */
36334
36335 Roo.bootstrap.RadioSet = function(config){
36336     
36337     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36338     
36339     this.radioes = [];
36340     
36341     Roo.bootstrap.RadioSet.register(this);
36342     
36343     this.addEvents({
36344         /**
36345         * @event check
36346         * Fires when the element is checked or unchecked.
36347         * @param {Roo.bootstrap.RadioSet} this This radio
36348         * @param {Roo.bootstrap.Radio} item The checked item
36349         */
36350        check : true,
36351        /**
36352         * @event click
36353         * Fires when the element is click.
36354         * @param {Roo.bootstrap.RadioSet} this This radio set
36355         * @param {Roo.bootstrap.Radio} item The checked item
36356         * @param {Roo.EventObject} e The event object
36357         */
36358        click : true
36359     });
36360     
36361 };
36362
36363 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36364
36365     radioes : false,
36366     
36367     inline : true,
36368     
36369     weight : '',
36370     
36371     indicatorpos : 'left',
36372     
36373     getAutoCreate : function()
36374     {
36375         var label = {
36376             tag : 'label',
36377             cls : 'roo-radio-set-label',
36378             cn : [
36379                 {
36380                     tag : 'span',
36381                     html : this.fieldLabel
36382                 }
36383             ]
36384         };
36385         if (Roo.bootstrap.version == 3) {
36386             
36387             
36388             if(this.indicatorpos == 'left'){
36389                 label.cn.unshift({
36390                     tag : 'i',
36391                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36392                     tooltip : 'This field is required'
36393                 });
36394             } else {
36395                 label.cn.push({
36396                     tag : 'i',
36397                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36398                     tooltip : 'This field is required'
36399                 });
36400             }
36401         }
36402         var items = {
36403             tag : 'div',
36404             cls : 'roo-radio-set-items'
36405         };
36406         
36407         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36408         
36409         if (align === 'left' && this.fieldLabel.length) {
36410             
36411             items = {
36412                 cls : "roo-radio-set-right", 
36413                 cn: [
36414                     items
36415                 ]
36416             };
36417             
36418             if(this.labelWidth > 12){
36419                 label.style = "width: " + this.labelWidth + 'px';
36420             }
36421             
36422             if(this.labelWidth < 13 && this.labelmd == 0){
36423                 this.labelmd = this.labelWidth;
36424             }
36425             
36426             if(this.labellg > 0){
36427                 label.cls += ' col-lg-' + this.labellg;
36428                 items.cls += ' col-lg-' + (12 - this.labellg);
36429             }
36430             
36431             if(this.labelmd > 0){
36432                 label.cls += ' col-md-' + this.labelmd;
36433                 items.cls += ' col-md-' + (12 - this.labelmd);
36434             }
36435             
36436             if(this.labelsm > 0){
36437                 label.cls += ' col-sm-' + this.labelsm;
36438                 items.cls += ' col-sm-' + (12 - this.labelsm);
36439             }
36440             
36441             if(this.labelxs > 0){
36442                 label.cls += ' col-xs-' + this.labelxs;
36443                 items.cls += ' col-xs-' + (12 - this.labelxs);
36444             }
36445         }
36446         
36447         var cfg = {
36448             tag : 'div',
36449             cls : 'roo-radio-set',
36450             cn : [
36451                 {
36452                     tag : 'input',
36453                     cls : 'roo-radio-set-input',
36454                     type : 'hidden',
36455                     name : this.name,
36456                     value : this.value ? this.value :  ''
36457                 },
36458                 label,
36459                 items
36460             ]
36461         };
36462         
36463         if(this.weight.length){
36464             cfg.cls += ' roo-radio-' + this.weight;
36465         }
36466         
36467         if(this.inline) {
36468             cfg.cls += ' roo-radio-set-inline';
36469         }
36470         
36471         var settings=this;
36472         ['xs','sm','md','lg'].map(function(size){
36473             if (settings[size]) {
36474                 cfg.cls += ' col-' + size + '-' + settings[size];
36475             }
36476         });
36477         
36478         return cfg;
36479         
36480     },
36481
36482     initEvents : function()
36483     {
36484         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36485         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36486         
36487         if(!this.fieldLabel.length){
36488             this.labelEl.hide();
36489         }
36490         
36491         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36492         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36493         
36494         this.indicator = this.indicatorEl();
36495         
36496         if(this.indicator){
36497             this.indicator.addClass('invisible');
36498         }
36499         
36500         this.originalValue = this.getValue();
36501         
36502     },
36503     
36504     inputEl: function ()
36505     {
36506         return this.el.select('.roo-radio-set-input', true).first();
36507     },
36508     
36509     getChildContainer : function()
36510     {
36511         return this.itemsEl;
36512     },
36513     
36514     register : function(item)
36515     {
36516         this.radioes.push(item);
36517         
36518     },
36519     
36520     validate : function()
36521     {   
36522         if(this.getVisibilityEl().hasClass('hidden')){
36523             return true;
36524         }
36525         
36526         var valid = false;
36527         
36528         Roo.each(this.radioes, function(i){
36529             if(!i.checked){
36530                 return;
36531             }
36532             
36533             valid = true;
36534             return false;
36535         });
36536         
36537         if(this.allowBlank) {
36538             return true;
36539         }
36540         
36541         if(this.disabled || valid){
36542             this.markValid();
36543             return true;
36544         }
36545         
36546         this.markInvalid();
36547         return false;
36548         
36549     },
36550     
36551     markValid : function()
36552     {
36553         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36554             this.indicatorEl().removeClass('visible');
36555             this.indicatorEl().addClass('invisible');
36556         }
36557         
36558         
36559         if (Roo.bootstrap.version == 3) {
36560             this.el.removeClass([this.invalidClass, this.validClass]);
36561             this.el.addClass(this.validClass);
36562         } else {
36563             this.el.removeClass(['is-invalid','is-valid']);
36564             this.el.addClass(['is-valid']);
36565         }
36566         this.fireEvent('valid', this);
36567     },
36568     
36569     markInvalid : function(msg)
36570     {
36571         if(this.allowBlank || this.disabled){
36572             return;
36573         }
36574         
36575         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36576             this.indicatorEl().removeClass('invisible');
36577             this.indicatorEl().addClass('visible');
36578         }
36579         if (Roo.bootstrap.version == 3) {
36580             this.el.removeClass([this.invalidClass, this.validClass]);
36581             this.el.addClass(this.invalidClass);
36582         } else {
36583             this.el.removeClass(['is-invalid','is-valid']);
36584             this.el.addClass(['is-invalid']);
36585         }
36586         
36587         this.fireEvent('invalid', this, msg);
36588         
36589     },
36590     
36591     setValue : function(v, suppressEvent)
36592     {   
36593         if(this.value === v){
36594             return;
36595         }
36596         
36597         this.value = v;
36598         
36599         if(this.rendered){
36600             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36601         }
36602         
36603         Roo.each(this.radioes, function(i){
36604             i.checked = false;
36605             i.el.removeClass('checked');
36606         });
36607         
36608         Roo.each(this.radioes, function(i){
36609             
36610             if(i.value === v || i.value.toString() === v.toString()){
36611                 i.checked = true;
36612                 i.el.addClass('checked');
36613                 
36614                 if(suppressEvent !== true){
36615                     this.fireEvent('check', this, i);
36616                 }
36617                 
36618                 return false;
36619             }
36620             
36621         }, this);
36622         
36623         this.validate();
36624     },
36625     
36626     clearInvalid : function(){
36627         
36628         if(!this.el || this.preventMark){
36629             return;
36630         }
36631         
36632         this.el.removeClass([this.invalidClass]);
36633         
36634         this.fireEvent('valid', this);
36635     }
36636     
36637 });
36638
36639 Roo.apply(Roo.bootstrap.RadioSet, {
36640     
36641     groups: {},
36642     
36643     register : function(set)
36644     {
36645         this.groups[set.name] = set;
36646     },
36647     
36648     get: function(name) 
36649     {
36650         if (typeof(this.groups[name]) == 'undefined') {
36651             return false;
36652         }
36653         
36654         return this.groups[name] ;
36655     }
36656     
36657 });
36658 /*
36659  * Based on:
36660  * Ext JS Library 1.1.1
36661  * Copyright(c) 2006-2007, Ext JS, LLC.
36662  *
36663  * Originally Released Under LGPL - original licence link has changed is not relivant.
36664  *
36665  * Fork - LGPL
36666  * <script type="text/javascript">
36667  */
36668
36669
36670 /**
36671  * @class Roo.bootstrap.SplitBar
36672  * @extends Roo.util.Observable
36673  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36674  * <br><br>
36675  * Usage:
36676  * <pre><code>
36677 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36678                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36679 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36680 split.minSize = 100;
36681 split.maxSize = 600;
36682 split.animate = true;
36683 split.on('moved', splitterMoved);
36684 </code></pre>
36685  * @constructor
36686  * Create a new SplitBar
36687  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36688  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36689  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36690  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36691                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36692                         position of the SplitBar).
36693  */
36694 Roo.bootstrap.SplitBar = function(cfg){
36695     
36696     /** @private */
36697     
36698     //{
36699     //  dragElement : elm
36700     //  resizingElement: el,
36701         // optional..
36702     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36703     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36704         // existingProxy ???
36705     //}
36706     
36707     this.el = Roo.get(cfg.dragElement, true);
36708     this.el.dom.unselectable = "on";
36709     /** @private */
36710     this.resizingEl = Roo.get(cfg.resizingElement, true);
36711
36712     /**
36713      * @private
36714      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36715      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36716      * @type Number
36717      */
36718     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36719     
36720     /**
36721      * The minimum size of the resizing element. (Defaults to 0)
36722      * @type Number
36723      */
36724     this.minSize = 0;
36725     
36726     /**
36727      * The maximum size of the resizing element. (Defaults to 2000)
36728      * @type Number
36729      */
36730     this.maxSize = 2000;
36731     
36732     /**
36733      * Whether to animate the transition to the new size
36734      * @type Boolean
36735      */
36736     this.animate = false;
36737     
36738     /**
36739      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36740      * @type Boolean
36741      */
36742     this.useShim = false;
36743     
36744     /** @private */
36745     this.shim = null;
36746     
36747     if(!cfg.existingProxy){
36748         /** @private */
36749         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36750     }else{
36751         this.proxy = Roo.get(cfg.existingProxy).dom;
36752     }
36753     /** @private */
36754     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36755     
36756     /** @private */
36757     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36758     
36759     /** @private */
36760     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36761     
36762     /** @private */
36763     this.dragSpecs = {};
36764     
36765     /**
36766      * @private The adapter to use to positon and resize elements
36767      */
36768     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36769     this.adapter.init(this);
36770     
36771     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36772         /** @private */
36773         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36774         this.el.addClass("roo-splitbar-h");
36775     }else{
36776         /** @private */
36777         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36778         this.el.addClass("roo-splitbar-v");
36779     }
36780     
36781     this.addEvents({
36782         /**
36783          * @event resize
36784          * Fires when the splitter is moved (alias for {@link #event-moved})
36785          * @param {Roo.bootstrap.SplitBar} this
36786          * @param {Number} newSize the new width or height
36787          */
36788         "resize" : true,
36789         /**
36790          * @event moved
36791          * Fires when the splitter is moved
36792          * @param {Roo.bootstrap.SplitBar} this
36793          * @param {Number} newSize the new width or height
36794          */
36795         "moved" : true,
36796         /**
36797          * @event beforeresize
36798          * Fires before the splitter is dragged
36799          * @param {Roo.bootstrap.SplitBar} this
36800          */
36801         "beforeresize" : true,
36802
36803         "beforeapply" : true
36804     });
36805
36806     Roo.util.Observable.call(this);
36807 };
36808
36809 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36810     onStartProxyDrag : function(x, y){
36811         this.fireEvent("beforeresize", this);
36812         if(!this.overlay){
36813             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36814             o.unselectable();
36815             o.enableDisplayMode("block");
36816             // all splitbars share the same overlay
36817             Roo.bootstrap.SplitBar.prototype.overlay = o;
36818         }
36819         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36820         this.overlay.show();
36821         Roo.get(this.proxy).setDisplayed("block");
36822         var size = this.adapter.getElementSize(this);
36823         this.activeMinSize = this.getMinimumSize();;
36824         this.activeMaxSize = this.getMaximumSize();;
36825         var c1 = size - this.activeMinSize;
36826         var c2 = Math.max(this.activeMaxSize - size, 0);
36827         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36828             this.dd.resetConstraints();
36829             this.dd.setXConstraint(
36830                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36831                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36832             );
36833             this.dd.setYConstraint(0, 0);
36834         }else{
36835             this.dd.resetConstraints();
36836             this.dd.setXConstraint(0, 0);
36837             this.dd.setYConstraint(
36838                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36839                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36840             );
36841          }
36842         this.dragSpecs.startSize = size;
36843         this.dragSpecs.startPoint = [x, y];
36844         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36845     },
36846     
36847     /** 
36848      * @private Called after the drag operation by the DDProxy
36849      */
36850     onEndProxyDrag : function(e){
36851         Roo.get(this.proxy).setDisplayed(false);
36852         var endPoint = Roo.lib.Event.getXY(e);
36853         if(this.overlay){
36854             this.overlay.hide();
36855         }
36856         var newSize;
36857         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36858             newSize = this.dragSpecs.startSize + 
36859                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36860                     endPoint[0] - this.dragSpecs.startPoint[0] :
36861                     this.dragSpecs.startPoint[0] - endPoint[0]
36862                 );
36863         }else{
36864             newSize = this.dragSpecs.startSize + 
36865                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36866                     endPoint[1] - this.dragSpecs.startPoint[1] :
36867                     this.dragSpecs.startPoint[1] - endPoint[1]
36868                 );
36869         }
36870         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36871         if(newSize != this.dragSpecs.startSize){
36872             if(this.fireEvent('beforeapply', this, newSize) !== false){
36873                 this.adapter.setElementSize(this, newSize);
36874                 this.fireEvent("moved", this, newSize);
36875                 this.fireEvent("resize", this, newSize);
36876             }
36877         }
36878     },
36879     
36880     /**
36881      * Get the adapter this SplitBar uses
36882      * @return The adapter object
36883      */
36884     getAdapter : function(){
36885         return this.adapter;
36886     },
36887     
36888     /**
36889      * Set the adapter this SplitBar uses
36890      * @param {Object} adapter A SplitBar adapter object
36891      */
36892     setAdapter : function(adapter){
36893         this.adapter = adapter;
36894         this.adapter.init(this);
36895     },
36896     
36897     /**
36898      * Gets the minimum size for the resizing element
36899      * @return {Number} The minimum size
36900      */
36901     getMinimumSize : function(){
36902         return this.minSize;
36903     },
36904     
36905     /**
36906      * Sets the minimum size for the resizing element
36907      * @param {Number} minSize The minimum size
36908      */
36909     setMinimumSize : function(minSize){
36910         this.minSize = minSize;
36911     },
36912     
36913     /**
36914      * Gets the maximum size for the resizing element
36915      * @return {Number} The maximum size
36916      */
36917     getMaximumSize : function(){
36918         return this.maxSize;
36919     },
36920     
36921     /**
36922      * Sets the maximum size for the resizing element
36923      * @param {Number} maxSize The maximum size
36924      */
36925     setMaximumSize : function(maxSize){
36926         this.maxSize = maxSize;
36927     },
36928     
36929     /**
36930      * Sets the initialize size for the resizing element
36931      * @param {Number} size The initial size
36932      */
36933     setCurrentSize : function(size){
36934         var oldAnimate = this.animate;
36935         this.animate = false;
36936         this.adapter.setElementSize(this, size);
36937         this.animate = oldAnimate;
36938     },
36939     
36940     /**
36941      * Destroy this splitbar. 
36942      * @param {Boolean} removeEl True to remove the element
36943      */
36944     destroy : function(removeEl){
36945         if(this.shim){
36946             this.shim.remove();
36947         }
36948         this.dd.unreg();
36949         this.proxy.parentNode.removeChild(this.proxy);
36950         if(removeEl){
36951             this.el.remove();
36952         }
36953     }
36954 });
36955
36956 /**
36957  * @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.
36958  */
36959 Roo.bootstrap.SplitBar.createProxy = function(dir){
36960     var proxy = new Roo.Element(document.createElement("div"));
36961     proxy.unselectable();
36962     var cls = 'roo-splitbar-proxy';
36963     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36964     document.body.appendChild(proxy.dom);
36965     return proxy.dom;
36966 };
36967
36968 /** 
36969  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36970  * Default Adapter. It assumes the splitter and resizing element are not positioned
36971  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36972  */
36973 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36974 };
36975
36976 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36977     // do nothing for now
36978     init : function(s){
36979     
36980     },
36981     /**
36982      * Called before drag operations to get the current size of the resizing element. 
36983      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36984      */
36985      getElementSize : function(s){
36986         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36987             return s.resizingEl.getWidth();
36988         }else{
36989             return s.resizingEl.getHeight();
36990         }
36991     },
36992     
36993     /**
36994      * Called after drag operations to set the size of the resizing element.
36995      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36996      * @param {Number} newSize The new size to set
36997      * @param {Function} onComplete A function to be invoked when resizing is complete
36998      */
36999     setElementSize : function(s, newSize, onComplete){
37000         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37001             if(!s.animate){
37002                 s.resizingEl.setWidth(newSize);
37003                 if(onComplete){
37004                     onComplete(s, newSize);
37005                 }
37006             }else{
37007                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37008             }
37009         }else{
37010             
37011             if(!s.animate){
37012                 s.resizingEl.setHeight(newSize);
37013                 if(onComplete){
37014                     onComplete(s, newSize);
37015                 }
37016             }else{
37017                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37018             }
37019         }
37020     }
37021 };
37022
37023 /** 
37024  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37025  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37026  * Adapter that  moves the splitter element to align with the resized sizing element. 
37027  * Used with an absolute positioned SplitBar.
37028  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37029  * document.body, make sure you assign an id to the body element.
37030  */
37031 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37032     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37033     this.container = Roo.get(container);
37034 };
37035
37036 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37037     init : function(s){
37038         this.basic.init(s);
37039     },
37040     
37041     getElementSize : function(s){
37042         return this.basic.getElementSize(s);
37043     },
37044     
37045     setElementSize : function(s, newSize, onComplete){
37046         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37047     },
37048     
37049     moveSplitter : function(s){
37050         var yes = Roo.bootstrap.SplitBar;
37051         switch(s.placement){
37052             case yes.LEFT:
37053                 s.el.setX(s.resizingEl.getRight());
37054                 break;
37055             case yes.RIGHT:
37056                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37057                 break;
37058             case yes.TOP:
37059                 s.el.setY(s.resizingEl.getBottom());
37060                 break;
37061             case yes.BOTTOM:
37062                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37063                 break;
37064         }
37065     }
37066 };
37067
37068 /**
37069  * Orientation constant - Create a vertical SplitBar
37070  * @static
37071  * @type Number
37072  */
37073 Roo.bootstrap.SplitBar.VERTICAL = 1;
37074
37075 /**
37076  * Orientation constant - Create a horizontal SplitBar
37077  * @static
37078  * @type Number
37079  */
37080 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37081
37082 /**
37083  * Placement constant - The resizing element is to the left of the splitter element
37084  * @static
37085  * @type Number
37086  */
37087 Roo.bootstrap.SplitBar.LEFT = 1;
37088
37089 /**
37090  * Placement constant - The resizing element is to the right of the splitter element
37091  * @static
37092  * @type Number
37093  */
37094 Roo.bootstrap.SplitBar.RIGHT = 2;
37095
37096 /**
37097  * Placement constant - The resizing element is positioned above the splitter element
37098  * @static
37099  * @type Number
37100  */
37101 Roo.bootstrap.SplitBar.TOP = 3;
37102
37103 /**
37104  * Placement constant - The resizing element is positioned under splitter element
37105  * @static
37106  * @type Number
37107  */
37108 Roo.bootstrap.SplitBar.BOTTOM = 4;
37109 Roo.namespace("Roo.bootstrap.layout");/*
37110  * Based on:
37111  * Ext JS Library 1.1.1
37112  * Copyright(c) 2006-2007, Ext JS, LLC.
37113  *
37114  * Originally Released Under LGPL - original licence link has changed is not relivant.
37115  *
37116  * Fork - LGPL
37117  * <script type="text/javascript">
37118  */
37119
37120 /**
37121  * @class Roo.bootstrap.layout.Manager
37122  * @extends Roo.bootstrap.Component
37123  * Base class for layout managers.
37124  */
37125 Roo.bootstrap.layout.Manager = function(config)
37126 {
37127     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37128
37129
37130
37131
37132
37133     /** false to disable window resize monitoring @type Boolean */
37134     this.monitorWindowResize = true;
37135     this.regions = {};
37136     this.addEvents({
37137         /**
37138          * @event layout
37139          * Fires when a layout is performed.
37140          * @param {Roo.LayoutManager} this
37141          */
37142         "layout" : true,
37143         /**
37144          * @event regionresized
37145          * Fires when the user resizes a region.
37146          * @param {Roo.LayoutRegion} region The resized region
37147          * @param {Number} newSize The new size (width for east/west, height for north/south)
37148          */
37149         "regionresized" : true,
37150         /**
37151          * @event regioncollapsed
37152          * Fires when a region is collapsed.
37153          * @param {Roo.LayoutRegion} region The collapsed region
37154          */
37155         "regioncollapsed" : true,
37156         /**
37157          * @event regionexpanded
37158          * Fires when a region is expanded.
37159          * @param {Roo.LayoutRegion} region The expanded region
37160          */
37161         "regionexpanded" : true
37162     });
37163     this.updating = false;
37164
37165     if (config.el) {
37166         this.el = Roo.get(config.el);
37167         this.initEvents();
37168     }
37169
37170 };
37171
37172 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37173
37174
37175     regions : null,
37176
37177     monitorWindowResize : true,
37178
37179
37180     updating : false,
37181
37182
37183     onRender : function(ct, position)
37184     {
37185         if(!this.el){
37186             this.el = Roo.get(ct);
37187             this.initEvents();
37188         }
37189         //this.fireEvent('render',this);
37190     },
37191
37192
37193     initEvents: function()
37194     {
37195
37196
37197         // ie scrollbar fix
37198         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37199             document.body.scroll = "no";
37200         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37201             this.el.position('relative');
37202         }
37203         this.id = this.el.id;
37204         this.el.addClass("roo-layout-container");
37205         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37206         if(this.el.dom != document.body ) {
37207             this.el.on('resize', this.layout,this);
37208             this.el.on('show', this.layout,this);
37209         }
37210
37211     },
37212
37213     /**
37214      * Returns true if this layout is currently being updated
37215      * @return {Boolean}
37216      */
37217     isUpdating : function(){
37218         return this.updating;
37219     },
37220
37221     /**
37222      * Suspend the LayoutManager from doing auto-layouts while
37223      * making multiple add or remove calls
37224      */
37225     beginUpdate : function(){
37226         this.updating = true;
37227     },
37228
37229     /**
37230      * Restore auto-layouts and optionally disable the manager from performing a layout
37231      * @param {Boolean} noLayout true to disable a layout update
37232      */
37233     endUpdate : function(noLayout){
37234         this.updating = false;
37235         if(!noLayout){
37236             this.layout();
37237         }
37238     },
37239
37240     layout: function(){
37241         // abstract...
37242     },
37243
37244     onRegionResized : function(region, newSize){
37245         this.fireEvent("regionresized", region, newSize);
37246         this.layout();
37247     },
37248
37249     onRegionCollapsed : function(region){
37250         this.fireEvent("regioncollapsed", region);
37251     },
37252
37253     onRegionExpanded : function(region){
37254         this.fireEvent("regionexpanded", region);
37255     },
37256
37257     /**
37258      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37259      * performs box-model adjustments.
37260      * @return {Object} The size as an object {width: (the width), height: (the height)}
37261      */
37262     getViewSize : function()
37263     {
37264         var size;
37265         if(this.el.dom != document.body){
37266             size = this.el.getSize();
37267         }else{
37268             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37269         }
37270         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37271         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37272         return size;
37273     },
37274
37275     /**
37276      * Returns the Element this layout is bound to.
37277      * @return {Roo.Element}
37278      */
37279     getEl : function(){
37280         return this.el;
37281     },
37282
37283     /**
37284      * Returns the specified region.
37285      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37286      * @return {Roo.LayoutRegion}
37287      */
37288     getRegion : function(target){
37289         return this.regions[target.toLowerCase()];
37290     },
37291
37292     onWindowResize : function(){
37293         if(this.monitorWindowResize){
37294             this.layout();
37295         }
37296     }
37297 });
37298 /*
37299  * Based on:
37300  * Ext JS Library 1.1.1
37301  * Copyright(c) 2006-2007, Ext JS, LLC.
37302  *
37303  * Originally Released Under LGPL - original licence link has changed is not relivant.
37304  *
37305  * Fork - LGPL
37306  * <script type="text/javascript">
37307  */
37308 /**
37309  * @class Roo.bootstrap.layout.Border
37310  * @extends Roo.bootstrap.layout.Manager
37311  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37312  * please see: examples/bootstrap/nested.html<br><br>
37313  
37314 <b>The container the layout is rendered into can be either the body element or any other element.
37315 If it is not the body element, the container needs to either be an absolute positioned element,
37316 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37317 the container size if it is not the body element.</b>
37318
37319 * @constructor
37320 * Create a new Border
37321 * @param {Object} config Configuration options
37322  */
37323 Roo.bootstrap.layout.Border = function(config){
37324     config = config || {};
37325     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37326     
37327     
37328     
37329     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37330         if(config[region]){
37331             config[region].region = region;
37332             this.addRegion(config[region]);
37333         }
37334     },this);
37335     
37336 };
37337
37338 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37339
37340 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37341     
37342     parent : false, // this might point to a 'nest' or a ???
37343     
37344     /**
37345      * Creates and adds a new region if it doesn't already exist.
37346      * @param {String} target The target region key (north, south, east, west or center).
37347      * @param {Object} config The regions config object
37348      * @return {BorderLayoutRegion} The new region
37349      */
37350     addRegion : function(config)
37351     {
37352         if(!this.regions[config.region]){
37353             var r = this.factory(config);
37354             this.bindRegion(r);
37355         }
37356         return this.regions[config.region];
37357     },
37358
37359     // private (kinda)
37360     bindRegion : function(r){
37361         this.regions[r.config.region] = r;
37362         
37363         r.on("visibilitychange",    this.layout, this);
37364         r.on("paneladded",          this.layout, this);
37365         r.on("panelremoved",        this.layout, this);
37366         r.on("invalidated",         this.layout, this);
37367         r.on("resized",             this.onRegionResized, this);
37368         r.on("collapsed",           this.onRegionCollapsed, this);
37369         r.on("expanded",            this.onRegionExpanded, this);
37370     },
37371
37372     /**
37373      * Performs a layout update.
37374      */
37375     layout : function()
37376     {
37377         if(this.updating) {
37378             return;
37379         }
37380         
37381         // render all the rebions if they have not been done alreayd?
37382         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37383             if(this.regions[region] && !this.regions[region].bodyEl){
37384                 this.regions[region].onRender(this.el)
37385             }
37386         },this);
37387         
37388         var size = this.getViewSize();
37389         var w = size.width;
37390         var h = size.height;
37391         var centerW = w;
37392         var centerH = h;
37393         var centerY = 0;
37394         var centerX = 0;
37395         //var x = 0, y = 0;
37396
37397         var rs = this.regions;
37398         var north = rs["north"];
37399         var south = rs["south"]; 
37400         var west = rs["west"];
37401         var east = rs["east"];
37402         var center = rs["center"];
37403         //if(this.hideOnLayout){ // not supported anymore
37404             //c.el.setStyle("display", "none");
37405         //}
37406         if(north && north.isVisible()){
37407             var b = north.getBox();
37408             var m = north.getMargins();
37409             b.width = w - (m.left+m.right);
37410             b.x = m.left;
37411             b.y = m.top;
37412             centerY = b.height + b.y + m.bottom;
37413             centerH -= centerY;
37414             north.updateBox(this.safeBox(b));
37415         }
37416         if(south && south.isVisible()){
37417             var b = south.getBox();
37418             var m = south.getMargins();
37419             b.width = w - (m.left+m.right);
37420             b.x = m.left;
37421             var totalHeight = (b.height + m.top + m.bottom);
37422             b.y = h - totalHeight + m.top;
37423             centerH -= totalHeight;
37424             south.updateBox(this.safeBox(b));
37425         }
37426         if(west && west.isVisible()){
37427             var b = west.getBox();
37428             var m = west.getMargins();
37429             b.height = centerH - (m.top+m.bottom);
37430             b.x = m.left;
37431             b.y = centerY + m.top;
37432             var totalWidth = (b.width + m.left + m.right);
37433             centerX += totalWidth;
37434             centerW -= totalWidth;
37435             west.updateBox(this.safeBox(b));
37436         }
37437         if(east && east.isVisible()){
37438             var b = east.getBox();
37439             var m = east.getMargins();
37440             b.height = centerH - (m.top+m.bottom);
37441             var totalWidth = (b.width + m.left + m.right);
37442             b.x = w - totalWidth + m.left;
37443             b.y = centerY + m.top;
37444             centerW -= totalWidth;
37445             east.updateBox(this.safeBox(b));
37446         }
37447         if(center){
37448             var m = center.getMargins();
37449             var centerBox = {
37450                 x: centerX + m.left,
37451                 y: centerY + m.top,
37452                 width: centerW - (m.left+m.right),
37453                 height: centerH - (m.top+m.bottom)
37454             };
37455             //if(this.hideOnLayout){
37456                 //center.el.setStyle("display", "block");
37457             //}
37458             center.updateBox(this.safeBox(centerBox));
37459         }
37460         this.el.repaint();
37461         this.fireEvent("layout", this);
37462     },
37463
37464     // private
37465     safeBox : function(box){
37466         box.width = Math.max(0, box.width);
37467         box.height = Math.max(0, box.height);
37468         return box;
37469     },
37470
37471     /**
37472      * Adds a ContentPanel (or subclass) to this layout.
37473      * @param {String} target The target region key (north, south, east, west or center).
37474      * @param {Roo.ContentPanel} panel The panel to add
37475      * @return {Roo.ContentPanel} The added panel
37476      */
37477     add : function(target, panel){
37478          
37479         target = target.toLowerCase();
37480         return this.regions[target].add(panel);
37481     },
37482
37483     /**
37484      * Remove a ContentPanel (or subclass) to this layout.
37485      * @param {String} target The target region key (north, south, east, west or center).
37486      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37487      * @return {Roo.ContentPanel} The removed panel
37488      */
37489     remove : function(target, panel){
37490         target = target.toLowerCase();
37491         return this.regions[target].remove(panel);
37492     },
37493
37494     /**
37495      * Searches all regions for a panel with the specified id
37496      * @param {String} panelId
37497      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37498      */
37499     findPanel : function(panelId){
37500         var rs = this.regions;
37501         for(var target in rs){
37502             if(typeof rs[target] != "function"){
37503                 var p = rs[target].getPanel(panelId);
37504                 if(p){
37505                     return p;
37506                 }
37507             }
37508         }
37509         return null;
37510     },
37511
37512     /**
37513      * Searches all regions for a panel with the specified id and activates (shows) it.
37514      * @param {String/ContentPanel} panelId The panels id or the panel itself
37515      * @return {Roo.ContentPanel} The shown panel or null
37516      */
37517     showPanel : function(panelId) {
37518       var rs = this.regions;
37519       for(var target in rs){
37520          var r = rs[target];
37521          if(typeof r != "function"){
37522             if(r.hasPanel(panelId)){
37523                return r.showPanel(panelId);
37524             }
37525          }
37526       }
37527       return null;
37528    },
37529
37530    /**
37531      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37532      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37533      */
37534    /*
37535     restoreState : function(provider){
37536         if(!provider){
37537             provider = Roo.state.Manager;
37538         }
37539         var sm = new Roo.LayoutStateManager();
37540         sm.init(this, provider);
37541     },
37542 */
37543  
37544  
37545     /**
37546      * Adds a xtype elements to the layout.
37547      * <pre><code>
37548
37549 layout.addxtype({
37550        xtype : 'ContentPanel',
37551        region: 'west',
37552        items: [ .... ]
37553    }
37554 );
37555
37556 layout.addxtype({
37557         xtype : 'NestedLayoutPanel',
37558         region: 'west',
37559         layout: {
37560            center: { },
37561            west: { }   
37562         },
37563         items : [ ... list of content panels or nested layout panels.. ]
37564    }
37565 );
37566 </code></pre>
37567      * @param {Object} cfg Xtype definition of item to add.
37568      */
37569     addxtype : function(cfg)
37570     {
37571         // basically accepts a pannel...
37572         // can accept a layout region..!?!?
37573         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37574         
37575         
37576         // theory?  children can only be panels??
37577         
37578         //if (!cfg.xtype.match(/Panel$/)) {
37579         //    return false;
37580         //}
37581         var ret = false;
37582         
37583         if (typeof(cfg.region) == 'undefined') {
37584             Roo.log("Failed to add Panel, region was not set");
37585             Roo.log(cfg);
37586             return false;
37587         }
37588         var region = cfg.region;
37589         delete cfg.region;
37590         
37591           
37592         var xitems = [];
37593         if (cfg.items) {
37594             xitems = cfg.items;
37595             delete cfg.items;
37596         }
37597         var nb = false;
37598         
37599         if ( region == 'center') {
37600             Roo.log("Center: " + cfg.title);
37601         }
37602         
37603         
37604         switch(cfg.xtype) 
37605         {
37606             case 'Content':  // ContentPanel (el, cfg)
37607             case 'Scroll':  // ContentPanel (el, cfg)
37608             case 'View': 
37609                 cfg.autoCreate = cfg.autoCreate || true;
37610                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37611                 //} else {
37612                 //    var el = this.el.createChild();
37613                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37614                 //}
37615                 
37616                 this.add(region, ret);
37617                 break;
37618             
37619             /*
37620             case 'TreePanel': // our new panel!
37621                 cfg.el = this.el.createChild();
37622                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37623                 this.add(region, ret);
37624                 break;
37625             */
37626             
37627             case 'Nest': 
37628                 // create a new Layout (which is  a Border Layout...
37629                 
37630                 var clayout = cfg.layout;
37631                 clayout.el  = this.el.createChild();
37632                 clayout.items   = clayout.items  || [];
37633                 
37634                 delete cfg.layout;
37635                 
37636                 // replace this exitems with the clayout ones..
37637                 xitems = clayout.items;
37638                  
37639                 // force background off if it's in center...
37640                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37641                     cfg.background = false;
37642                 }
37643                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37644                 
37645                 
37646                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37647                 //console.log('adding nested layout panel '  + cfg.toSource());
37648                 this.add(region, ret);
37649                 nb = {}; /// find first...
37650                 break;
37651             
37652             case 'Grid':
37653                 
37654                 // needs grid and region
37655                 
37656                 //var el = this.getRegion(region).el.createChild();
37657                 /*
37658                  *var el = this.el.createChild();
37659                 // create the grid first...
37660                 cfg.grid.container = el;
37661                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37662                 */
37663                 
37664                 if (region == 'center' && this.active ) {
37665                     cfg.background = false;
37666                 }
37667                 
37668                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37669                 
37670                 this.add(region, ret);
37671                 /*
37672                 if (cfg.background) {
37673                     // render grid on panel activation (if panel background)
37674                     ret.on('activate', function(gp) {
37675                         if (!gp.grid.rendered) {
37676                     //        gp.grid.render(el);
37677                         }
37678                     });
37679                 } else {
37680                   //  cfg.grid.render(el);
37681                 }
37682                 */
37683                 break;
37684            
37685            
37686             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37687                 // it was the old xcomponent building that caused this before.
37688                 // espeically if border is the top element in the tree.
37689                 ret = this;
37690                 break; 
37691                 
37692                     
37693                 
37694                 
37695                 
37696             default:
37697                 /*
37698                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37699                     
37700                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37701                     this.add(region, ret);
37702                 } else {
37703                 */
37704                     Roo.log(cfg);
37705                     throw "Can not add '" + cfg.xtype + "' to Border";
37706                     return null;
37707              
37708                                 
37709              
37710         }
37711         this.beginUpdate();
37712         // add children..
37713         var region = '';
37714         var abn = {};
37715         Roo.each(xitems, function(i)  {
37716             region = nb && i.region ? i.region : false;
37717             
37718             var add = ret.addxtype(i);
37719            
37720             if (region) {
37721                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37722                 if (!i.background) {
37723                     abn[region] = nb[region] ;
37724                 }
37725             }
37726             
37727         });
37728         this.endUpdate();
37729
37730         // make the last non-background panel active..
37731         //if (nb) { Roo.log(abn); }
37732         if (nb) {
37733             
37734             for(var r in abn) {
37735                 region = this.getRegion(r);
37736                 if (region) {
37737                     // tried using nb[r], but it does not work..
37738                      
37739                     region.showPanel(abn[r]);
37740                    
37741                 }
37742             }
37743         }
37744         return ret;
37745         
37746     },
37747     
37748     
37749 // private
37750     factory : function(cfg)
37751     {
37752         
37753         var validRegions = Roo.bootstrap.layout.Border.regions;
37754
37755         var target = cfg.region;
37756         cfg.mgr = this;
37757         
37758         var r = Roo.bootstrap.layout;
37759         Roo.log(target);
37760         switch(target){
37761             case "north":
37762                 return new r.North(cfg);
37763             case "south":
37764                 return new r.South(cfg);
37765             case "east":
37766                 return new r.East(cfg);
37767             case "west":
37768                 return new r.West(cfg);
37769             case "center":
37770                 return new r.Center(cfg);
37771         }
37772         throw 'Layout region "'+target+'" not supported.';
37773     }
37774     
37775     
37776 });
37777  /*
37778  * Based on:
37779  * Ext JS Library 1.1.1
37780  * Copyright(c) 2006-2007, Ext JS, LLC.
37781  *
37782  * Originally Released Under LGPL - original licence link has changed is not relivant.
37783  *
37784  * Fork - LGPL
37785  * <script type="text/javascript">
37786  */
37787  
37788 /**
37789  * @class Roo.bootstrap.layout.Basic
37790  * @extends Roo.util.Observable
37791  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37792  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37793  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37794  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37795  * @cfg {string}   region  the region that it inhabits..
37796  * @cfg {bool}   skipConfig skip config?
37797  * 
37798
37799  */
37800 Roo.bootstrap.layout.Basic = function(config){
37801     
37802     this.mgr = config.mgr;
37803     
37804     this.position = config.region;
37805     
37806     var skipConfig = config.skipConfig;
37807     
37808     this.events = {
37809         /**
37810          * @scope Roo.BasicLayoutRegion
37811          */
37812         
37813         /**
37814          * @event beforeremove
37815          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37816          * @param {Roo.LayoutRegion} this
37817          * @param {Roo.ContentPanel} panel The panel
37818          * @param {Object} e The cancel event object
37819          */
37820         "beforeremove" : true,
37821         /**
37822          * @event invalidated
37823          * Fires when the layout for this region is changed.
37824          * @param {Roo.LayoutRegion} this
37825          */
37826         "invalidated" : true,
37827         /**
37828          * @event visibilitychange
37829          * Fires when this region is shown or hidden 
37830          * @param {Roo.LayoutRegion} this
37831          * @param {Boolean} visibility true or false
37832          */
37833         "visibilitychange" : true,
37834         /**
37835          * @event paneladded
37836          * Fires when a panel is added. 
37837          * @param {Roo.LayoutRegion} this
37838          * @param {Roo.ContentPanel} panel The panel
37839          */
37840         "paneladded" : true,
37841         /**
37842          * @event panelremoved
37843          * Fires when a panel is removed. 
37844          * @param {Roo.LayoutRegion} this
37845          * @param {Roo.ContentPanel} panel The panel
37846          */
37847         "panelremoved" : true,
37848         /**
37849          * @event beforecollapse
37850          * Fires when this region before collapse.
37851          * @param {Roo.LayoutRegion} this
37852          */
37853         "beforecollapse" : true,
37854         /**
37855          * @event collapsed
37856          * Fires when this region is collapsed.
37857          * @param {Roo.LayoutRegion} this
37858          */
37859         "collapsed" : true,
37860         /**
37861          * @event expanded
37862          * Fires when this region is expanded.
37863          * @param {Roo.LayoutRegion} this
37864          */
37865         "expanded" : true,
37866         /**
37867          * @event slideshow
37868          * Fires when this region is slid into view.
37869          * @param {Roo.LayoutRegion} this
37870          */
37871         "slideshow" : true,
37872         /**
37873          * @event slidehide
37874          * Fires when this region slides out of view. 
37875          * @param {Roo.LayoutRegion} this
37876          */
37877         "slidehide" : true,
37878         /**
37879          * @event panelactivated
37880          * Fires when a panel is activated. 
37881          * @param {Roo.LayoutRegion} this
37882          * @param {Roo.ContentPanel} panel The activated panel
37883          */
37884         "panelactivated" : true,
37885         /**
37886          * @event resized
37887          * Fires when the user resizes this region. 
37888          * @param {Roo.LayoutRegion} this
37889          * @param {Number} newSize The new size (width for east/west, height for north/south)
37890          */
37891         "resized" : true
37892     };
37893     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37894     this.panels = new Roo.util.MixedCollection();
37895     this.panels.getKey = this.getPanelId.createDelegate(this);
37896     this.box = null;
37897     this.activePanel = null;
37898     // ensure listeners are added...
37899     
37900     if (config.listeners || config.events) {
37901         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37902             listeners : config.listeners || {},
37903             events : config.events || {}
37904         });
37905     }
37906     
37907     if(skipConfig !== true){
37908         this.applyConfig(config);
37909     }
37910 };
37911
37912 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37913 {
37914     getPanelId : function(p){
37915         return p.getId();
37916     },
37917     
37918     applyConfig : function(config){
37919         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37920         this.config = config;
37921         
37922     },
37923     
37924     /**
37925      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37926      * the width, for horizontal (north, south) the height.
37927      * @param {Number} newSize The new width or height
37928      */
37929     resizeTo : function(newSize){
37930         var el = this.el ? this.el :
37931                  (this.activePanel ? this.activePanel.getEl() : null);
37932         if(el){
37933             switch(this.position){
37934                 case "east":
37935                 case "west":
37936                     el.setWidth(newSize);
37937                     this.fireEvent("resized", this, newSize);
37938                 break;
37939                 case "north":
37940                 case "south":
37941                     el.setHeight(newSize);
37942                     this.fireEvent("resized", this, newSize);
37943                 break;                
37944             }
37945         }
37946     },
37947     
37948     getBox : function(){
37949         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37950     },
37951     
37952     getMargins : function(){
37953         return this.margins;
37954     },
37955     
37956     updateBox : function(box){
37957         this.box = box;
37958         var el = this.activePanel.getEl();
37959         el.dom.style.left = box.x + "px";
37960         el.dom.style.top = box.y + "px";
37961         this.activePanel.setSize(box.width, box.height);
37962     },
37963     
37964     /**
37965      * Returns the container element for this region.
37966      * @return {Roo.Element}
37967      */
37968     getEl : function(){
37969         return this.activePanel;
37970     },
37971     
37972     /**
37973      * Returns true if this region is currently visible.
37974      * @return {Boolean}
37975      */
37976     isVisible : function(){
37977         return this.activePanel ? true : false;
37978     },
37979     
37980     setActivePanel : function(panel){
37981         panel = this.getPanel(panel);
37982         if(this.activePanel && this.activePanel != panel){
37983             this.activePanel.setActiveState(false);
37984             this.activePanel.getEl().setLeftTop(-10000,-10000);
37985         }
37986         this.activePanel = panel;
37987         panel.setActiveState(true);
37988         if(this.box){
37989             panel.setSize(this.box.width, this.box.height);
37990         }
37991         this.fireEvent("panelactivated", this, panel);
37992         this.fireEvent("invalidated");
37993     },
37994     
37995     /**
37996      * Show the specified panel.
37997      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37998      * @return {Roo.ContentPanel} The shown panel or null
37999      */
38000     showPanel : function(panel){
38001         panel = this.getPanel(panel);
38002         if(panel){
38003             this.setActivePanel(panel);
38004         }
38005         return panel;
38006     },
38007     
38008     /**
38009      * Get the active panel for this region.
38010      * @return {Roo.ContentPanel} The active panel or null
38011      */
38012     getActivePanel : function(){
38013         return this.activePanel;
38014     },
38015     
38016     /**
38017      * Add the passed ContentPanel(s)
38018      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38019      * @return {Roo.ContentPanel} The panel added (if only one was added)
38020      */
38021     add : function(panel){
38022         if(arguments.length > 1){
38023             for(var i = 0, len = arguments.length; i < len; i++) {
38024                 this.add(arguments[i]);
38025             }
38026             return null;
38027         }
38028         if(this.hasPanel(panel)){
38029             this.showPanel(panel);
38030             return panel;
38031         }
38032         var el = panel.getEl();
38033         if(el.dom.parentNode != this.mgr.el.dom){
38034             this.mgr.el.dom.appendChild(el.dom);
38035         }
38036         if(panel.setRegion){
38037             panel.setRegion(this);
38038         }
38039         this.panels.add(panel);
38040         el.setStyle("position", "absolute");
38041         if(!panel.background){
38042             this.setActivePanel(panel);
38043             if(this.config.initialSize && this.panels.getCount()==1){
38044                 this.resizeTo(this.config.initialSize);
38045             }
38046         }
38047         this.fireEvent("paneladded", this, panel);
38048         return panel;
38049     },
38050     
38051     /**
38052      * Returns true if the panel is in this region.
38053      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38054      * @return {Boolean}
38055      */
38056     hasPanel : function(panel){
38057         if(typeof panel == "object"){ // must be panel obj
38058             panel = panel.getId();
38059         }
38060         return this.getPanel(panel) ? true : false;
38061     },
38062     
38063     /**
38064      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38065      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38066      * @param {Boolean} preservePanel Overrides the config preservePanel option
38067      * @return {Roo.ContentPanel} The panel that was removed
38068      */
38069     remove : function(panel, preservePanel){
38070         panel = this.getPanel(panel);
38071         if(!panel){
38072             return null;
38073         }
38074         var e = {};
38075         this.fireEvent("beforeremove", this, panel, e);
38076         if(e.cancel === true){
38077             return null;
38078         }
38079         var panelId = panel.getId();
38080         this.panels.removeKey(panelId);
38081         return panel;
38082     },
38083     
38084     /**
38085      * Returns the panel specified or null if it's not in this region.
38086      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38087      * @return {Roo.ContentPanel}
38088      */
38089     getPanel : function(id){
38090         if(typeof id == "object"){ // must be panel obj
38091             return id;
38092         }
38093         return this.panels.get(id);
38094     },
38095     
38096     /**
38097      * Returns this regions position (north/south/east/west/center).
38098      * @return {String} 
38099      */
38100     getPosition: function(){
38101         return this.position;    
38102     }
38103 });/*
38104  * Based on:
38105  * Ext JS Library 1.1.1
38106  * Copyright(c) 2006-2007, Ext JS, LLC.
38107  *
38108  * Originally Released Under LGPL - original licence link has changed is not relivant.
38109  *
38110  * Fork - LGPL
38111  * <script type="text/javascript">
38112  */
38113  
38114 /**
38115  * @class Roo.bootstrap.layout.Region
38116  * @extends Roo.bootstrap.layout.Basic
38117  * This class represents a region in a layout manager.
38118  
38119  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38120  * @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})
38121  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38122  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38123  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38124  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38125  * @cfg {String}    title           The title for the region (overrides panel titles)
38126  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38127  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38128  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38129  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38130  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38131  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38132  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38133  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38134  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38135  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38136
38137  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38138  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38139  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38140  * @cfg {Number}    width           For East/West panels
38141  * @cfg {Number}    height          For North/South panels
38142  * @cfg {Boolean}   split           To show the splitter
38143  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38144  * 
38145  * @cfg {string}   cls             Extra CSS classes to add to region
38146  * 
38147  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38148  * @cfg {string}   region  the region that it inhabits..
38149  *
38150
38151  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38152  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38153
38154  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38155  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38156  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38157  */
38158 Roo.bootstrap.layout.Region = function(config)
38159 {
38160     this.applyConfig(config);
38161
38162     var mgr = config.mgr;
38163     var pos = config.region;
38164     config.skipConfig = true;
38165     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38166     
38167     if (mgr.el) {
38168         this.onRender(mgr.el);   
38169     }
38170      
38171     this.visible = true;
38172     this.collapsed = false;
38173     this.unrendered_panels = [];
38174 };
38175
38176 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38177
38178     position: '', // set by wrapper (eg. north/south etc..)
38179     unrendered_panels : null,  // unrendered panels.
38180     
38181     tabPosition : false,
38182     
38183     mgr: false, // points to 'Border'
38184     
38185     
38186     createBody : function(){
38187         /** This region's body element 
38188         * @type Roo.Element */
38189         this.bodyEl = this.el.createChild({
38190                 tag: "div",
38191                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38192         });
38193     },
38194
38195     onRender: function(ctr, pos)
38196     {
38197         var dh = Roo.DomHelper;
38198         /** This region's container element 
38199         * @type Roo.Element */
38200         this.el = dh.append(ctr.dom, {
38201                 tag: "div",
38202                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38203             }, true);
38204         /** This region's title element 
38205         * @type Roo.Element */
38206     
38207         this.titleEl = dh.append(this.el.dom,  {
38208                 tag: "div",
38209                 unselectable: "on",
38210                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38211                 children:[
38212                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38213                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38214                 ]
38215             }, true);
38216         
38217         this.titleEl.enableDisplayMode();
38218         /** This region's title text element 
38219         * @type HTMLElement */
38220         this.titleTextEl = this.titleEl.dom.firstChild;
38221         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38222         /*
38223         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38224         this.closeBtn.enableDisplayMode();
38225         this.closeBtn.on("click", this.closeClicked, this);
38226         this.closeBtn.hide();
38227     */
38228         this.createBody(this.config);
38229         if(this.config.hideWhenEmpty){
38230             this.hide();
38231             this.on("paneladded", this.validateVisibility, this);
38232             this.on("panelremoved", this.validateVisibility, this);
38233         }
38234         if(this.autoScroll){
38235             this.bodyEl.setStyle("overflow", "auto");
38236         }else{
38237             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38238         }
38239         //if(c.titlebar !== false){
38240             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38241                 this.titleEl.hide();
38242             }else{
38243                 this.titleEl.show();
38244                 if(this.config.title){
38245                     this.titleTextEl.innerHTML = this.config.title;
38246                 }
38247             }
38248         //}
38249         if(this.config.collapsed){
38250             this.collapse(true);
38251         }
38252         if(this.config.hidden){
38253             this.hide();
38254         }
38255         
38256         if (this.unrendered_panels && this.unrendered_panels.length) {
38257             for (var i =0;i< this.unrendered_panels.length; i++) {
38258                 this.add(this.unrendered_panels[i]);
38259             }
38260             this.unrendered_panels = null;
38261             
38262         }
38263         
38264     },
38265     
38266     applyConfig : function(c)
38267     {
38268         /*
38269          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38270             var dh = Roo.DomHelper;
38271             if(c.titlebar !== false){
38272                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38273                 this.collapseBtn.on("click", this.collapse, this);
38274                 this.collapseBtn.enableDisplayMode();
38275                 /*
38276                 if(c.showPin === true || this.showPin){
38277                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38278                     this.stickBtn.enableDisplayMode();
38279                     this.stickBtn.on("click", this.expand, this);
38280                     this.stickBtn.hide();
38281                 }
38282                 
38283             }
38284             */
38285             /** This region's collapsed element
38286             * @type Roo.Element */
38287             /*
38288              *
38289             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38290                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38291             ]}, true);
38292             
38293             if(c.floatable !== false){
38294                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38295                this.collapsedEl.on("click", this.collapseClick, this);
38296             }
38297
38298             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38299                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38300                    id: "message", unselectable: "on", style:{"float":"left"}});
38301                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38302              }
38303             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38304             this.expandBtn.on("click", this.expand, this);
38305             
38306         }
38307         
38308         if(this.collapseBtn){
38309             this.collapseBtn.setVisible(c.collapsible == true);
38310         }
38311         
38312         this.cmargins = c.cmargins || this.cmargins ||
38313                          (this.position == "west" || this.position == "east" ?
38314                              {top: 0, left: 2, right:2, bottom: 0} :
38315                              {top: 2, left: 0, right:0, bottom: 2});
38316         */
38317         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38318         
38319         
38320         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38321         
38322         this.autoScroll = c.autoScroll || false;
38323         
38324         
38325        
38326         
38327         this.duration = c.duration || .30;
38328         this.slideDuration = c.slideDuration || .45;
38329         this.config = c;
38330        
38331     },
38332     /**
38333      * Returns true if this region is currently visible.
38334      * @return {Boolean}
38335      */
38336     isVisible : function(){
38337         return this.visible;
38338     },
38339
38340     /**
38341      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38342      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38343      */
38344     //setCollapsedTitle : function(title){
38345     //    title = title || "&#160;";
38346      //   if(this.collapsedTitleTextEl){
38347       //      this.collapsedTitleTextEl.innerHTML = title;
38348        // }
38349     //},
38350
38351     getBox : function(){
38352         var b;
38353       //  if(!this.collapsed){
38354             b = this.el.getBox(false, true);
38355        // }else{
38356           //  b = this.collapsedEl.getBox(false, true);
38357         //}
38358         return b;
38359     },
38360
38361     getMargins : function(){
38362         return this.margins;
38363         //return this.collapsed ? this.cmargins : this.margins;
38364     },
38365 /*
38366     highlight : function(){
38367         this.el.addClass("x-layout-panel-dragover");
38368     },
38369
38370     unhighlight : function(){
38371         this.el.removeClass("x-layout-panel-dragover");
38372     },
38373 */
38374     updateBox : function(box)
38375     {
38376         if (!this.bodyEl) {
38377             return; // not rendered yet..
38378         }
38379         
38380         this.box = box;
38381         if(!this.collapsed){
38382             this.el.dom.style.left = box.x + "px";
38383             this.el.dom.style.top = box.y + "px";
38384             this.updateBody(box.width, box.height);
38385         }else{
38386             this.collapsedEl.dom.style.left = box.x + "px";
38387             this.collapsedEl.dom.style.top = box.y + "px";
38388             this.collapsedEl.setSize(box.width, box.height);
38389         }
38390         if(this.tabs){
38391             this.tabs.autoSizeTabs();
38392         }
38393     },
38394
38395     updateBody : function(w, h)
38396     {
38397         if(w !== null){
38398             this.el.setWidth(w);
38399             w -= this.el.getBorderWidth("rl");
38400             if(this.config.adjustments){
38401                 w += this.config.adjustments[0];
38402             }
38403         }
38404         if(h !== null && h > 0){
38405             this.el.setHeight(h);
38406             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38407             h -= this.el.getBorderWidth("tb");
38408             if(this.config.adjustments){
38409                 h += this.config.adjustments[1];
38410             }
38411             this.bodyEl.setHeight(h);
38412             if(this.tabs){
38413                 h = this.tabs.syncHeight(h);
38414             }
38415         }
38416         if(this.panelSize){
38417             w = w !== null ? w : this.panelSize.width;
38418             h = h !== null ? h : this.panelSize.height;
38419         }
38420         if(this.activePanel){
38421             var el = this.activePanel.getEl();
38422             w = w !== null ? w : el.getWidth();
38423             h = h !== null ? h : el.getHeight();
38424             this.panelSize = {width: w, height: h};
38425             this.activePanel.setSize(w, h);
38426         }
38427         if(Roo.isIE && this.tabs){
38428             this.tabs.el.repaint();
38429         }
38430     },
38431
38432     /**
38433      * Returns the container element for this region.
38434      * @return {Roo.Element}
38435      */
38436     getEl : function(){
38437         return this.el;
38438     },
38439
38440     /**
38441      * Hides this region.
38442      */
38443     hide : function(){
38444         //if(!this.collapsed){
38445             this.el.dom.style.left = "-2000px";
38446             this.el.hide();
38447         //}else{
38448          //   this.collapsedEl.dom.style.left = "-2000px";
38449          //   this.collapsedEl.hide();
38450        // }
38451         this.visible = false;
38452         this.fireEvent("visibilitychange", this, false);
38453     },
38454
38455     /**
38456      * Shows this region if it was previously hidden.
38457      */
38458     show : function(){
38459         //if(!this.collapsed){
38460             this.el.show();
38461         //}else{
38462         //    this.collapsedEl.show();
38463        // }
38464         this.visible = true;
38465         this.fireEvent("visibilitychange", this, true);
38466     },
38467 /*
38468     closeClicked : function(){
38469         if(this.activePanel){
38470             this.remove(this.activePanel);
38471         }
38472     },
38473
38474     collapseClick : function(e){
38475         if(this.isSlid){
38476            e.stopPropagation();
38477            this.slideIn();
38478         }else{
38479            e.stopPropagation();
38480            this.slideOut();
38481         }
38482     },
38483 */
38484     /**
38485      * Collapses this region.
38486      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38487      */
38488     /*
38489     collapse : function(skipAnim, skipCheck = false){
38490         if(this.collapsed) {
38491             return;
38492         }
38493         
38494         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38495             
38496             this.collapsed = true;
38497             if(this.split){
38498                 this.split.el.hide();
38499             }
38500             if(this.config.animate && skipAnim !== true){
38501                 this.fireEvent("invalidated", this);
38502                 this.animateCollapse();
38503             }else{
38504                 this.el.setLocation(-20000,-20000);
38505                 this.el.hide();
38506                 this.collapsedEl.show();
38507                 this.fireEvent("collapsed", this);
38508                 this.fireEvent("invalidated", this);
38509             }
38510         }
38511         
38512     },
38513 */
38514     animateCollapse : function(){
38515         // overridden
38516     },
38517
38518     /**
38519      * Expands this region if it was previously collapsed.
38520      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38521      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38522      */
38523     /*
38524     expand : function(e, skipAnim){
38525         if(e) {
38526             e.stopPropagation();
38527         }
38528         if(!this.collapsed || this.el.hasActiveFx()) {
38529             return;
38530         }
38531         if(this.isSlid){
38532             this.afterSlideIn();
38533             skipAnim = true;
38534         }
38535         this.collapsed = false;
38536         if(this.config.animate && skipAnim !== true){
38537             this.animateExpand();
38538         }else{
38539             this.el.show();
38540             if(this.split){
38541                 this.split.el.show();
38542             }
38543             this.collapsedEl.setLocation(-2000,-2000);
38544             this.collapsedEl.hide();
38545             this.fireEvent("invalidated", this);
38546             this.fireEvent("expanded", this);
38547         }
38548     },
38549 */
38550     animateExpand : function(){
38551         // overridden
38552     },
38553
38554     initTabs : function()
38555     {
38556         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38557         
38558         var ts = new Roo.bootstrap.panel.Tabs({
38559             el: this.bodyEl.dom,
38560             region : this,
38561             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38562             disableTooltips: this.config.disableTabTips,
38563             toolbar : this.config.toolbar
38564         });
38565         
38566         if(this.config.hideTabs){
38567             ts.stripWrap.setDisplayed(false);
38568         }
38569         this.tabs = ts;
38570         ts.resizeTabs = this.config.resizeTabs === true;
38571         ts.minTabWidth = this.config.minTabWidth || 40;
38572         ts.maxTabWidth = this.config.maxTabWidth || 250;
38573         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38574         ts.monitorResize = false;
38575         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38576         ts.bodyEl.addClass('roo-layout-tabs-body');
38577         this.panels.each(this.initPanelAsTab, this);
38578     },
38579
38580     initPanelAsTab : function(panel){
38581         var ti = this.tabs.addTab(
38582             panel.getEl().id,
38583             panel.getTitle(),
38584             null,
38585             this.config.closeOnTab && panel.isClosable(),
38586             panel.tpl
38587         );
38588         if(panel.tabTip !== undefined){
38589             ti.setTooltip(panel.tabTip);
38590         }
38591         ti.on("activate", function(){
38592               this.setActivePanel(panel);
38593         }, this);
38594         
38595         if(this.config.closeOnTab){
38596             ti.on("beforeclose", function(t, e){
38597                 e.cancel = true;
38598                 this.remove(panel);
38599             }, this);
38600         }
38601         
38602         panel.tabItem = ti;
38603         
38604         return ti;
38605     },
38606
38607     updatePanelTitle : function(panel, title)
38608     {
38609         if(this.activePanel == panel){
38610             this.updateTitle(title);
38611         }
38612         if(this.tabs){
38613             var ti = this.tabs.getTab(panel.getEl().id);
38614             ti.setText(title);
38615             if(panel.tabTip !== undefined){
38616                 ti.setTooltip(panel.tabTip);
38617             }
38618         }
38619     },
38620
38621     updateTitle : function(title){
38622         if(this.titleTextEl && !this.config.title){
38623             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38624         }
38625     },
38626
38627     setActivePanel : function(panel)
38628     {
38629         panel = this.getPanel(panel);
38630         if(this.activePanel && this.activePanel != panel){
38631             if(this.activePanel.setActiveState(false) === false){
38632                 return;
38633             }
38634         }
38635         this.activePanel = panel;
38636         panel.setActiveState(true);
38637         if(this.panelSize){
38638             panel.setSize(this.panelSize.width, this.panelSize.height);
38639         }
38640         if(this.closeBtn){
38641             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38642         }
38643         this.updateTitle(panel.getTitle());
38644         if(this.tabs){
38645             this.fireEvent("invalidated", this);
38646         }
38647         this.fireEvent("panelactivated", this, panel);
38648     },
38649
38650     /**
38651      * Shows the specified panel.
38652      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38653      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38654      */
38655     showPanel : function(panel)
38656     {
38657         panel = this.getPanel(panel);
38658         if(panel){
38659             if(this.tabs){
38660                 var tab = this.tabs.getTab(panel.getEl().id);
38661                 if(tab.isHidden()){
38662                     this.tabs.unhideTab(tab.id);
38663                 }
38664                 tab.activate();
38665             }else{
38666                 this.setActivePanel(panel);
38667             }
38668         }
38669         return panel;
38670     },
38671
38672     /**
38673      * Get the active panel for this region.
38674      * @return {Roo.ContentPanel} The active panel or null
38675      */
38676     getActivePanel : function(){
38677         return this.activePanel;
38678     },
38679
38680     validateVisibility : function(){
38681         if(this.panels.getCount() < 1){
38682             this.updateTitle("&#160;");
38683             this.closeBtn.hide();
38684             this.hide();
38685         }else{
38686             if(!this.isVisible()){
38687                 this.show();
38688             }
38689         }
38690     },
38691
38692     /**
38693      * Adds the passed ContentPanel(s) to this region.
38694      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38695      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38696      */
38697     add : function(panel)
38698     {
38699         if(arguments.length > 1){
38700             for(var i = 0, len = arguments.length; i < len; i++) {
38701                 this.add(arguments[i]);
38702             }
38703             return null;
38704         }
38705         
38706         // if we have not been rendered yet, then we can not really do much of this..
38707         if (!this.bodyEl) {
38708             this.unrendered_panels.push(panel);
38709             return panel;
38710         }
38711         
38712         
38713         
38714         
38715         if(this.hasPanel(panel)){
38716             this.showPanel(panel);
38717             return panel;
38718         }
38719         panel.setRegion(this);
38720         this.panels.add(panel);
38721        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38722             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38723             // and hide them... ???
38724             this.bodyEl.dom.appendChild(panel.getEl().dom);
38725             if(panel.background !== true){
38726                 this.setActivePanel(panel);
38727             }
38728             this.fireEvent("paneladded", this, panel);
38729             return panel;
38730         }
38731         */
38732         if(!this.tabs){
38733             this.initTabs();
38734         }else{
38735             this.initPanelAsTab(panel);
38736         }
38737         
38738         
38739         if(panel.background !== true){
38740             this.tabs.activate(panel.getEl().id);
38741         }
38742         this.fireEvent("paneladded", this, panel);
38743         return panel;
38744     },
38745
38746     /**
38747      * Hides the tab for the specified panel.
38748      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38749      */
38750     hidePanel : function(panel){
38751         if(this.tabs && (panel = this.getPanel(panel))){
38752             this.tabs.hideTab(panel.getEl().id);
38753         }
38754     },
38755
38756     /**
38757      * Unhides the tab for a previously hidden panel.
38758      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38759      */
38760     unhidePanel : function(panel){
38761         if(this.tabs && (panel = this.getPanel(panel))){
38762             this.tabs.unhideTab(panel.getEl().id);
38763         }
38764     },
38765
38766     clearPanels : function(){
38767         while(this.panels.getCount() > 0){
38768              this.remove(this.panels.first());
38769         }
38770     },
38771
38772     /**
38773      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38774      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38775      * @param {Boolean} preservePanel Overrides the config preservePanel option
38776      * @return {Roo.ContentPanel} The panel that was removed
38777      */
38778     remove : function(panel, preservePanel)
38779     {
38780         panel = this.getPanel(panel);
38781         if(!panel){
38782             return null;
38783         }
38784         var e = {};
38785         this.fireEvent("beforeremove", this, panel, e);
38786         if(e.cancel === true){
38787             return null;
38788         }
38789         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38790         var panelId = panel.getId();
38791         this.panels.removeKey(panelId);
38792         if(preservePanel){
38793             document.body.appendChild(panel.getEl().dom);
38794         }
38795         if(this.tabs){
38796             this.tabs.removeTab(panel.getEl().id);
38797         }else if (!preservePanel){
38798             this.bodyEl.dom.removeChild(panel.getEl().dom);
38799         }
38800         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38801             var p = this.panels.first();
38802             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38803             tempEl.appendChild(p.getEl().dom);
38804             this.bodyEl.update("");
38805             this.bodyEl.dom.appendChild(p.getEl().dom);
38806             tempEl = null;
38807             this.updateTitle(p.getTitle());
38808             this.tabs = null;
38809             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38810             this.setActivePanel(p);
38811         }
38812         panel.setRegion(null);
38813         if(this.activePanel == panel){
38814             this.activePanel = null;
38815         }
38816         if(this.config.autoDestroy !== false && preservePanel !== true){
38817             try{panel.destroy();}catch(e){}
38818         }
38819         this.fireEvent("panelremoved", this, panel);
38820         return panel;
38821     },
38822
38823     /**
38824      * Returns the TabPanel component used by this region
38825      * @return {Roo.TabPanel}
38826      */
38827     getTabs : function(){
38828         return this.tabs;
38829     },
38830
38831     createTool : function(parentEl, className){
38832         var btn = Roo.DomHelper.append(parentEl, {
38833             tag: "div",
38834             cls: "x-layout-tools-button",
38835             children: [ {
38836                 tag: "div",
38837                 cls: "roo-layout-tools-button-inner " + className,
38838                 html: "&#160;"
38839             }]
38840         }, true);
38841         btn.addClassOnOver("roo-layout-tools-button-over");
38842         return btn;
38843     }
38844 });/*
38845  * Based on:
38846  * Ext JS Library 1.1.1
38847  * Copyright(c) 2006-2007, Ext JS, LLC.
38848  *
38849  * Originally Released Under LGPL - original licence link has changed is not relivant.
38850  *
38851  * Fork - LGPL
38852  * <script type="text/javascript">
38853  */
38854  
38855
38856
38857 /**
38858  * @class Roo.SplitLayoutRegion
38859  * @extends Roo.LayoutRegion
38860  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38861  */
38862 Roo.bootstrap.layout.Split = function(config){
38863     this.cursor = config.cursor;
38864     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38865 };
38866
38867 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38868 {
38869     splitTip : "Drag to resize.",
38870     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38871     useSplitTips : false,
38872
38873     applyConfig : function(config){
38874         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38875     },
38876     
38877     onRender : function(ctr,pos) {
38878         
38879         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38880         if(!this.config.split){
38881             return;
38882         }
38883         if(!this.split){
38884             
38885             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38886                             tag: "div",
38887                             id: this.el.id + "-split",
38888                             cls: "roo-layout-split roo-layout-split-"+this.position,
38889                             html: "&#160;"
38890             });
38891             /** The SplitBar for this region 
38892             * @type Roo.SplitBar */
38893             // does not exist yet...
38894             Roo.log([this.position, this.orientation]);
38895             
38896             this.split = new Roo.bootstrap.SplitBar({
38897                 dragElement : splitEl,
38898                 resizingElement: this.el,
38899                 orientation : this.orientation
38900             });
38901             
38902             this.split.on("moved", this.onSplitMove, this);
38903             this.split.useShim = this.config.useShim === true;
38904             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38905             if(this.useSplitTips){
38906                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38907             }
38908             //if(config.collapsible){
38909             //    this.split.el.on("dblclick", this.collapse,  this);
38910             //}
38911         }
38912         if(typeof this.config.minSize != "undefined"){
38913             this.split.minSize = this.config.minSize;
38914         }
38915         if(typeof this.config.maxSize != "undefined"){
38916             this.split.maxSize = this.config.maxSize;
38917         }
38918         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38919             this.hideSplitter();
38920         }
38921         
38922     },
38923
38924     getHMaxSize : function(){
38925          var cmax = this.config.maxSize || 10000;
38926          var center = this.mgr.getRegion("center");
38927          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38928     },
38929
38930     getVMaxSize : function(){
38931          var cmax = this.config.maxSize || 10000;
38932          var center = this.mgr.getRegion("center");
38933          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38934     },
38935
38936     onSplitMove : function(split, newSize){
38937         this.fireEvent("resized", this, newSize);
38938     },
38939     
38940     /** 
38941      * Returns the {@link Roo.SplitBar} for this region.
38942      * @return {Roo.SplitBar}
38943      */
38944     getSplitBar : function(){
38945         return this.split;
38946     },
38947     
38948     hide : function(){
38949         this.hideSplitter();
38950         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38951     },
38952
38953     hideSplitter : function(){
38954         if(this.split){
38955             this.split.el.setLocation(-2000,-2000);
38956             this.split.el.hide();
38957         }
38958     },
38959
38960     show : function(){
38961         if(this.split){
38962             this.split.el.show();
38963         }
38964         Roo.bootstrap.layout.Split.superclass.show.call(this);
38965     },
38966     
38967     beforeSlide: function(){
38968         if(Roo.isGecko){// firefox overflow auto bug workaround
38969             this.bodyEl.clip();
38970             if(this.tabs) {
38971                 this.tabs.bodyEl.clip();
38972             }
38973             if(this.activePanel){
38974                 this.activePanel.getEl().clip();
38975                 
38976                 if(this.activePanel.beforeSlide){
38977                     this.activePanel.beforeSlide();
38978                 }
38979             }
38980         }
38981     },
38982     
38983     afterSlide : function(){
38984         if(Roo.isGecko){// firefox overflow auto bug workaround
38985             this.bodyEl.unclip();
38986             if(this.tabs) {
38987                 this.tabs.bodyEl.unclip();
38988             }
38989             if(this.activePanel){
38990                 this.activePanel.getEl().unclip();
38991                 if(this.activePanel.afterSlide){
38992                     this.activePanel.afterSlide();
38993                 }
38994             }
38995         }
38996     },
38997
38998     initAutoHide : function(){
38999         if(this.autoHide !== false){
39000             if(!this.autoHideHd){
39001                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39002                 this.autoHideHd = {
39003                     "mouseout": function(e){
39004                         if(!e.within(this.el, true)){
39005                             st.delay(500);
39006                         }
39007                     },
39008                     "mouseover" : function(e){
39009                         st.cancel();
39010                     },
39011                     scope : this
39012                 };
39013             }
39014             this.el.on(this.autoHideHd);
39015         }
39016     },
39017
39018     clearAutoHide : function(){
39019         if(this.autoHide !== false){
39020             this.el.un("mouseout", this.autoHideHd.mouseout);
39021             this.el.un("mouseover", this.autoHideHd.mouseover);
39022         }
39023     },
39024
39025     clearMonitor : function(){
39026         Roo.get(document).un("click", this.slideInIf, this);
39027     },
39028
39029     // these names are backwards but not changed for compat
39030     slideOut : function(){
39031         if(this.isSlid || this.el.hasActiveFx()){
39032             return;
39033         }
39034         this.isSlid = true;
39035         if(this.collapseBtn){
39036             this.collapseBtn.hide();
39037         }
39038         this.closeBtnState = this.closeBtn.getStyle('display');
39039         this.closeBtn.hide();
39040         if(this.stickBtn){
39041             this.stickBtn.show();
39042         }
39043         this.el.show();
39044         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39045         this.beforeSlide();
39046         this.el.setStyle("z-index", 10001);
39047         this.el.slideIn(this.getSlideAnchor(), {
39048             callback: function(){
39049                 this.afterSlide();
39050                 this.initAutoHide();
39051                 Roo.get(document).on("click", this.slideInIf, this);
39052                 this.fireEvent("slideshow", this);
39053             },
39054             scope: this,
39055             block: true
39056         });
39057     },
39058
39059     afterSlideIn : function(){
39060         this.clearAutoHide();
39061         this.isSlid = false;
39062         this.clearMonitor();
39063         this.el.setStyle("z-index", "");
39064         if(this.collapseBtn){
39065             this.collapseBtn.show();
39066         }
39067         this.closeBtn.setStyle('display', this.closeBtnState);
39068         if(this.stickBtn){
39069             this.stickBtn.hide();
39070         }
39071         this.fireEvent("slidehide", this);
39072     },
39073
39074     slideIn : function(cb){
39075         if(!this.isSlid || this.el.hasActiveFx()){
39076             Roo.callback(cb);
39077             return;
39078         }
39079         this.isSlid = false;
39080         this.beforeSlide();
39081         this.el.slideOut(this.getSlideAnchor(), {
39082             callback: function(){
39083                 this.el.setLeftTop(-10000, -10000);
39084                 this.afterSlide();
39085                 this.afterSlideIn();
39086                 Roo.callback(cb);
39087             },
39088             scope: this,
39089             block: true
39090         });
39091     },
39092     
39093     slideInIf : function(e){
39094         if(!e.within(this.el)){
39095             this.slideIn();
39096         }
39097     },
39098
39099     animateCollapse : function(){
39100         this.beforeSlide();
39101         this.el.setStyle("z-index", 20000);
39102         var anchor = this.getSlideAnchor();
39103         this.el.slideOut(anchor, {
39104             callback : function(){
39105                 this.el.setStyle("z-index", "");
39106                 this.collapsedEl.slideIn(anchor, {duration:.3});
39107                 this.afterSlide();
39108                 this.el.setLocation(-10000,-10000);
39109                 this.el.hide();
39110                 this.fireEvent("collapsed", this);
39111             },
39112             scope: this,
39113             block: true
39114         });
39115     },
39116
39117     animateExpand : function(){
39118         this.beforeSlide();
39119         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39120         this.el.setStyle("z-index", 20000);
39121         this.collapsedEl.hide({
39122             duration:.1
39123         });
39124         this.el.slideIn(this.getSlideAnchor(), {
39125             callback : function(){
39126                 this.el.setStyle("z-index", "");
39127                 this.afterSlide();
39128                 if(this.split){
39129                     this.split.el.show();
39130                 }
39131                 this.fireEvent("invalidated", this);
39132                 this.fireEvent("expanded", this);
39133             },
39134             scope: this,
39135             block: true
39136         });
39137     },
39138
39139     anchors : {
39140         "west" : "left",
39141         "east" : "right",
39142         "north" : "top",
39143         "south" : "bottom"
39144     },
39145
39146     sanchors : {
39147         "west" : "l",
39148         "east" : "r",
39149         "north" : "t",
39150         "south" : "b"
39151     },
39152
39153     canchors : {
39154         "west" : "tl-tr",
39155         "east" : "tr-tl",
39156         "north" : "tl-bl",
39157         "south" : "bl-tl"
39158     },
39159
39160     getAnchor : function(){
39161         return this.anchors[this.position];
39162     },
39163
39164     getCollapseAnchor : function(){
39165         return this.canchors[this.position];
39166     },
39167
39168     getSlideAnchor : function(){
39169         return this.sanchors[this.position];
39170     },
39171
39172     getAlignAdj : function(){
39173         var cm = this.cmargins;
39174         switch(this.position){
39175             case "west":
39176                 return [0, 0];
39177             break;
39178             case "east":
39179                 return [0, 0];
39180             break;
39181             case "north":
39182                 return [0, 0];
39183             break;
39184             case "south":
39185                 return [0, 0];
39186             break;
39187         }
39188     },
39189
39190     getExpandAdj : function(){
39191         var c = this.collapsedEl, cm = this.cmargins;
39192         switch(this.position){
39193             case "west":
39194                 return [-(cm.right+c.getWidth()+cm.left), 0];
39195             break;
39196             case "east":
39197                 return [cm.right+c.getWidth()+cm.left, 0];
39198             break;
39199             case "north":
39200                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39201             break;
39202             case "south":
39203                 return [0, cm.top+cm.bottom+c.getHeight()];
39204             break;
39205         }
39206     }
39207 });/*
39208  * Based on:
39209  * Ext JS Library 1.1.1
39210  * Copyright(c) 2006-2007, Ext JS, LLC.
39211  *
39212  * Originally Released Under LGPL - original licence link has changed is not relivant.
39213  *
39214  * Fork - LGPL
39215  * <script type="text/javascript">
39216  */
39217 /*
39218  * These classes are private internal classes
39219  */
39220 Roo.bootstrap.layout.Center = function(config){
39221     config.region = "center";
39222     Roo.bootstrap.layout.Region.call(this, config);
39223     this.visible = true;
39224     this.minWidth = config.minWidth || 20;
39225     this.minHeight = config.minHeight || 20;
39226 };
39227
39228 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39229     hide : function(){
39230         // center panel can't be hidden
39231     },
39232     
39233     show : function(){
39234         // center panel can't be hidden
39235     },
39236     
39237     getMinWidth: function(){
39238         return this.minWidth;
39239     },
39240     
39241     getMinHeight: function(){
39242         return this.minHeight;
39243     }
39244 });
39245
39246
39247
39248
39249  
39250
39251
39252
39253
39254
39255
39256 Roo.bootstrap.layout.North = function(config)
39257 {
39258     config.region = 'north';
39259     config.cursor = 'n-resize';
39260     
39261     Roo.bootstrap.layout.Split.call(this, config);
39262     
39263     
39264     if(this.split){
39265         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39266         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39267         this.split.el.addClass("roo-layout-split-v");
39268     }
39269     var size = config.initialSize || config.height;
39270     if(typeof size != "undefined"){
39271         this.el.setHeight(size);
39272     }
39273 };
39274 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39275 {
39276     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39277     
39278     
39279     
39280     getBox : function(){
39281         if(this.collapsed){
39282             return this.collapsedEl.getBox();
39283         }
39284         var box = this.el.getBox();
39285         if(this.split){
39286             box.height += this.split.el.getHeight();
39287         }
39288         return box;
39289     },
39290     
39291     updateBox : function(box){
39292         if(this.split && !this.collapsed){
39293             box.height -= this.split.el.getHeight();
39294             this.split.el.setLeft(box.x);
39295             this.split.el.setTop(box.y+box.height);
39296             this.split.el.setWidth(box.width);
39297         }
39298         if(this.collapsed){
39299             this.updateBody(box.width, null);
39300         }
39301         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39302     }
39303 });
39304
39305
39306
39307
39308
39309 Roo.bootstrap.layout.South = function(config){
39310     config.region = 'south';
39311     config.cursor = 's-resize';
39312     Roo.bootstrap.layout.Split.call(this, config);
39313     if(this.split){
39314         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39315         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39316         this.split.el.addClass("roo-layout-split-v");
39317     }
39318     var size = config.initialSize || config.height;
39319     if(typeof size != "undefined"){
39320         this.el.setHeight(size);
39321     }
39322 };
39323
39324 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39325     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39326     getBox : function(){
39327         if(this.collapsed){
39328             return this.collapsedEl.getBox();
39329         }
39330         var box = this.el.getBox();
39331         if(this.split){
39332             var sh = this.split.el.getHeight();
39333             box.height += sh;
39334             box.y -= sh;
39335         }
39336         return box;
39337     },
39338     
39339     updateBox : function(box){
39340         if(this.split && !this.collapsed){
39341             var sh = this.split.el.getHeight();
39342             box.height -= sh;
39343             box.y += sh;
39344             this.split.el.setLeft(box.x);
39345             this.split.el.setTop(box.y-sh);
39346             this.split.el.setWidth(box.width);
39347         }
39348         if(this.collapsed){
39349             this.updateBody(box.width, null);
39350         }
39351         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39352     }
39353 });
39354
39355 Roo.bootstrap.layout.East = function(config){
39356     config.region = "east";
39357     config.cursor = "e-resize";
39358     Roo.bootstrap.layout.Split.call(this, config);
39359     if(this.split){
39360         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39361         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39362         this.split.el.addClass("roo-layout-split-h");
39363     }
39364     var size = config.initialSize || config.width;
39365     if(typeof size != "undefined"){
39366         this.el.setWidth(size);
39367     }
39368 };
39369 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39370     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39371     getBox : function(){
39372         if(this.collapsed){
39373             return this.collapsedEl.getBox();
39374         }
39375         var box = this.el.getBox();
39376         if(this.split){
39377             var sw = this.split.el.getWidth();
39378             box.width += sw;
39379             box.x -= sw;
39380         }
39381         return box;
39382     },
39383
39384     updateBox : function(box){
39385         if(this.split && !this.collapsed){
39386             var sw = this.split.el.getWidth();
39387             box.width -= sw;
39388             this.split.el.setLeft(box.x);
39389             this.split.el.setTop(box.y);
39390             this.split.el.setHeight(box.height);
39391             box.x += sw;
39392         }
39393         if(this.collapsed){
39394             this.updateBody(null, box.height);
39395         }
39396         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39397     }
39398 });
39399
39400 Roo.bootstrap.layout.West = function(config){
39401     config.region = "west";
39402     config.cursor = "w-resize";
39403     
39404     Roo.bootstrap.layout.Split.call(this, config);
39405     if(this.split){
39406         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39407         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39408         this.split.el.addClass("roo-layout-split-h");
39409     }
39410     
39411 };
39412 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39413     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39414     
39415     onRender: function(ctr, pos)
39416     {
39417         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39418         var size = this.config.initialSize || this.config.width;
39419         if(typeof size != "undefined"){
39420             this.el.setWidth(size);
39421         }
39422     },
39423     
39424     getBox : function(){
39425         if(this.collapsed){
39426             return this.collapsedEl.getBox();
39427         }
39428         var box = this.el.getBox();
39429         if(this.split){
39430             box.width += this.split.el.getWidth();
39431         }
39432         return box;
39433     },
39434     
39435     updateBox : function(box){
39436         if(this.split && !this.collapsed){
39437             var sw = this.split.el.getWidth();
39438             box.width -= sw;
39439             this.split.el.setLeft(box.x+box.width);
39440             this.split.el.setTop(box.y);
39441             this.split.el.setHeight(box.height);
39442         }
39443         if(this.collapsed){
39444             this.updateBody(null, box.height);
39445         }
39446         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39447     }
39448 });Roo.namespace("Roo.bootstrap.panel");/*
39449  * Based on:
39450  * Ext JS Library 1.1.1
39451  * Copyright(c) 2006-2007, Ext JS, LLC.
39452  *
39453  * Originally Released Under LGPL - original licence link has changed is not relivant.
39454  *
39455  * Fork - LGPL
39456  * <script type="text/javascript">
39457  */
39458 /**
39459  * @class Roo.ContentPanel
39460  * @extends Roo.util.Observable
39461  * A basic ContentPanel element.
39462  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39463  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39464  * @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
39465  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39466  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39467  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39468  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39469  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39470  * @cfg {String} title          The title for this panel
39471  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39472  * @cfg {String} url            Calls {@link #setUrl} with this value
39473  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39474  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39475  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39476  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39477  * @cfg {Boolean} badges render the badges
39478  * @cfg {String} cls  extra classes to use  
39479  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39480
39481  * @constructor
39482  * Create a new ContentPanel.
39483  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39484  * @param {String/Object} config A string to set only the title or a config object
39485  * @param {String} content (optional) Set the HTML content for this panel
39486  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39487  */
39488 Roo.bootstrap.panel.Content = function( config){
39489     
39490     this.tpl = config.tpl || false;
39491     
39492     var el = config.el;
39493     var content = config.content;
39494
39495     if(config.autoCreate){ // xtype is available if this is called from factory
39496         el = Roo.id();
39497     }
39498     this.el = Roo.get(el);
39499     if(!this.el && config && config.autoCreate){
39500         if(typeof config.autoCreate == "object"){
39501             if(!config.autoCreate.id){
39502                 config.autoCreate.id = config.id||el;
39503             }
39504             this.el = Roo.DomHelper.append(document.body,
39505                         config.autoCreate, true);
39506         }else{
39507             var elcfg =  {
39508                 tag: "div",
39509                 cls: (config.cls || '') +
39510                     (config.background ? ' bg-' + config.background : '') +
39511                     " roo-layout-inactive-content",
39512                 id: config.id||el
39513             };
39514             if (config.html) {
39515                 elcfg.html = config.html;
39516                 
39517             }
39518                         
39519             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39520         }
39521     } 
39522     this.closable = false;
39523     this.loaded = false;
39524     this.active = false;
39525    
39526       
39527     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39528         
39529         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39530         
39531         this.wrapEl = this.el; //this.el.wrap();
39532         var ti = [];
39533         if (config.toolbar.items) {
39534             ti = config.toolbar.items ;
39535             delete config.toolbar.items ;
39536         }
39537         
39538         var nitems = [];
39539         this.toolbar.render(this.wrapEl, 'before');
39540         for(var i =0;i < ti.length;i++) {
39541           //  Roo.log(['add child', items[i]]);
39542             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39543         }
39544         this.toolbar.items = nitems;
39545         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39546         delete config.toolbar;
39547         
39548     }
39549     /*
39550     // xtype created footer. - not sure if will work as we normally have to render first..
39551     if (this.footer && !this.footer.el && this.footer.xtype) {
39552         if (!this.wrapEl) {
39553             this.wrapEl = this.el.wrap();
39554         }
39555     
39556         this.footer.container = this.wrapEl.createChild();
39557          
39558         this.footer = Roo.factory(this.footer, Roo);
39559         
39560     }
39561     */
39562     
39563      if(typeof config == "string"){
39564         this.title = config;
39565     }else{
39566         Roo.apply(this, config);
39567     }
39568     
39569     if(this.resizeEl){
39570         this.resizeEl = Roo.get(this.resizeEl, true);
39571     }else{
39572         this.resizeEl = this.el;
39573     }
39574     // handle view.xtype
39575     
39576  
39577     
39578     
39579     this.addEvents({
39580         /**
39581          * @event activate
39582          * Fires when this panel is activated. 
39583          * @param {Roo.ContentPanel} this
39584          */
39585         "activate" : true,
39586         /**
39587          * @event deactivate
39588          * Fires when this panel is activated. 
39589          * @param {Roo.ContentPanel} this
39590          */
39591         "deactivate" : true,
39592
39593         /**
39594          * @event resize
39595          * Fires when this panel is resized if fitToFrame is true.
39596          * @param {Roo.ContentPanel} this
39597          * @param {Number} width The width after any component adjustments
39598          * @param {Number} height The height after any component adjustments
39599          */
39600         "resize" : true,
39601         
39602          /**
39603          * @event render
39604          * Fires when this tab is created
39605          * @param {Roo.ContentPanel} this
39606          */
39607         "render" : true
39608         
39609         
39610         
39611     });
39612     
39613
39614     
39615     
39616     if(this.autoScroll){
39617         this.resizeEl.setStyle("overflow", "auto");
39618     } else {
39619         // fix randome scrolling
39620         //this.el.on('scroll', function() {
39621         //    Roo.log('fix random scolling');
39622         //    this.scrollTo('top',0); 
39623         //});
39624     }
39625     content = content || this.content;
39626     if(content){
39627         this.setContent(content);
39628     }
39629     if(config && config.url){
39630         this.setUrl(this.url, this.params, this.loadOnce);
39631     }
39632     
39633     
39634     
39635     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39636     
39637     if (this.view && typeof(this.view.xtype) != 'undefined') {
39638         this.view.el = this.el.appendChild(document.createElement("div"));
39639         this.view = Roo.factory(this.view); 
39640         this.view.render  &&  this.view.render(false, '');  
39641     }
39642     
39643     
39644     this.fireEvent('render', this);
39645 };
39646
39647 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39648     
39649     cls : '',
39650     background : '',
39651     
39652     tabTip : '',
39653     
39654     setRegion : function(region){
39655         this.region = region;
39656         this.setActiveClass(region && !this.background);
39657     },
39658     
39659     
39660     setActiveClass: function(state)
39661     {
39662         if(state){
39663            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39664            this.el.setStyle('position','relative');
39665         }else{
39666            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39667            this.el.setStyle('position', 'absolute');
39668         } 
39669     },
39670     
39671     /**
39672      * Returns the toolbar for this Panel if one was configured. 
39673      * @return {Roo.Toolbar} 
39674      */
39675     getToolbar : function(){
39676         return this.toolbar;
39677     },
39678     
39679     setActiveState : function(active)
39680     {
39681         this.active = active;
39682         this.setActiveClass(active);
39683         if(!active){
39684             if(this.fireEvent("deactivate", this) === false){
39685                 return false;
39686             }
39687             return true;
39688         }
39689         this.fireEvent("activate", this);
39690         return true;
39691     },
39692     /**
39693      * Updates this panel's element
39694      * @param {String} content The new content
39695      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39696     */
39697     setContent : function(content, loadScripts){
39698         this.el.update(content, loadScripts);
39699     },
39700
39701     ignoreResize : function(w, h){
39702         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39703             return true;
39704         }else{
39705             this.lastSize = {width: w, height: h};
39706             return false;
39707         }
39708     },
39709     /**
39710      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39711      * @return {Roo.UpdateManager} The UpdateManager
39712      */
39713     getUpdateManager : function(){
39714         return this.el.getUpdateManager();
39715     },
39716      /**
39717      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39718      * @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:
39719 <pre><code>
39720 panel.load({
39721     url: "your-url.php",
39722     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39723     callback: yourFunction,
39724     scope: yourObject, //(optional scope)
39725     discardUrl: false,
39726     nocache: false,
39727     text: "Loading...",
39728     timeout: 30,
39729     scripts: false
39730 });
39731 </code></pre>
39732      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39733      * 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.
39734      * @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}
39735      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39736      * @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.
39737      * @return {Roo.ContentPanel} this
39738      */
39739     load : function(){
39740         var um = this.el.getUpdateManager();
39741         um.update.apply(um, arguments);
39742         return this;
39743     },
39744
39745
39746     /**
39747      * 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.
39748      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39749      * @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)
39750      * @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)
39751      * @return {Roo.UpdateManager} The UpdateManager
39752      */
39753     setUrl : function(url, params, loadOnce){
39754         if(this.refreshDelegate){
39755             this.removeListener("activate", this.refreshDelegate);
39756         }
39757         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39758         this.on("activate", this.refreshDelegate);
39759         return this.el.getUpdateManager();
39760     },
39761     
39762     _handleRefresh : function(url, params, loadOnce){
39763         if(!loadOnce || !this.loaded){
39764             var updater = this.el.getUpdateManager();
39765             updater.update(url, params, this._setLoaded.createDelegate(this));
39766         }
39767     },
39768     
39769     _setLoaded : function(){
39770         this.loaded = true;
39771     }, 
39772     
39773     /**
39774      * Returns this panel's id
39775      * @return {String} 
39776      */
39777     getId : function(){
39778         return this.el.id;
39779     },
39780     
39781     /** 
39782      * Returns this panel's element - used by regiosn to add.
39783      * @return {Roo.Element} 
39784      */
39785     getEl : function(){
39786         return this.wrapEl || this.el;
39787     },
39788     
39789    
39790     
39791     adjustForComponents : function(width, height)
39792     {
39793         //Roo.log('adjustForComponents ');
39794         if(this.resizeEl != this.el){
39795             width -= this.el.getFrameWidth('lr');
39796             height -= this.el.getFrameWidth('tb');
39797         }
39798         if(this.toolbar){
39799             var te = this.toolbar.getEl();
39800             te.setWidth(width);
39801             height -= te.getHeight();
39802         }
39803         if(this.footer){
39804             var te = this.footer.getEl();
39805             te.setWidth(width);
39806             height -= te.getHeight();
39807         }
39808         
39809         
39810         if(this.adjustments){
39811             width += this.adjustments[0];
39812             height += this.adjustments[1];
39813         }
39814         return {"width": width, "height": height};
39815     },
39816     
39817     setSize : function(width, height){
39818         if(this.fitToFrame && !this.ignoreResize(width, height)){
39819             if(this.fitContainer && this.resizeEl != this.el){
39820                 this.el.setSize(width, height);
39821             }
39822             var size = this.adjustForComponents(width, height);
39823             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39824             this.fireEvent('resize', this, size.width, size.height);
39825         }
39826     },
39827     
39828     /**
39829      * Returns this panel's title
39830      * @return {String} 
39831      */
39832     getTitle : function(){
39833         
39834         if (typeof(this.title) != 'object') {
39835             return this.title;
39836         }
39837         
39838         var t = '';
39839         for (var k in this.title) {
39840             if (!this.title.hasOwnProperty(k)) {
39841                 continue;
39842             }
39843             
39844             if (k.indexOf('-') >= 0) {
39845                 var s = k.split('-');
39846                 for (var i = 0; i<s.length; i++) {
39847                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39848                 }
39849             } else {
39850                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39851             }
39852         }
39853         return t;
39854     },
39855     
39856     /**
39857      * Set this panel's title
39858      * @param {String} title
39859      */
39860     setTitle : function(title){
39861         this.title = title;
39862         if(this.region){
39863             this.region.updatePanelTitle(this, title);
39864         }
39865     },
39866     
39867     /**
39868      * Returns true is this panel was configured to be closable
39869      * @return {Boolean} 
39870      */
39871     isClosable : function(){
39872         return this.closable;
39873     },
39874     
39875     beforeSlide : function(){
39876         this.el.clip();
39877         this.resizeEl.clip();
39878     },
39879     
39880     afterSlide : function(){
39881         this.el.unclip();
39882         this.resizeEl.unclip();
39883     },
39884     
39885     /**
39886      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39887      *   Will fail silently if the {@link #setUrl} method has not been called.
39888      *   This does not activate the panel, just updates its content.
39889      */
39890     refresh : function(){
39891         if(this.refreshDelegate){
39892            this.loaded = false;
39893            this.refreshDelegate();
39894         }
39895     },
39896     
39897     /**
39898      * Destroys this panel
39899      */
39900     destroy : function(){
39901         this.el.removeAllListeners();
39902         var tempEl = document.createElement("span");
39903         tempEl.appendChild(this.el.dom);
39904         tempEl.innerHTML = "";
39905         this.el.remove();
39906         this.el = null;
39907     },
39908     
39909     /**
39910      * form - if the content panel contains a form - this is a reference to it.
39911      * @type {Roo.form.Form}
39912      */
39913     form : false,
39914     /**
39915      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39916      *    This contains a reference to it.
39917      * @type {Roo.View}
39918      */
39919     view : false,
39920     
39921       /**
39922      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39923      * <pre><code>
39924
39925 layout.addxtype({
39926        xtype : 'Form',
39927        items: [ .... ]
39928    }
39929 );
39930
39931 </code></pre>
39932      * @param {Object} cfg Xtype definition of item to add.
39933      */
39934     
39935     
39936     getChildContainer: function () {
39937         return this.getEl();
39938     }
39939     
39940     
39941     /*
39942         var  ret = new Roo.factory(cfg);
39943         return ret;
39944         
39945         
39946         // add form..
39947         if (cfg.xtype.match(/^Form$/)) {
39948             
39949             var el;
39950             //if (this.footer) {
39951             //    el = this.footer.container.insertSibling(false, 'before');
39952             //} else {
39953                 el = this.el.createChild();
39954             //}
39955
39956             this.form = new  Roo.form.Form(cfg);
39957             
39958             
39959             if ( this.form.allItems.length) {
39960                 this.form.render(el.dom);
39961             }
39962             return this.form;
39963         }
39964         // should only have one of theses..
39965         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39966             // views.. should not be just added - used named prop 'view''
39967             
39968             cfg.el = this.el.appendChild(document.createElement("div"));
39969             // factory?
39970             
39971             var ret = new Roo.factory(cfg);
39972              
39973              ret.render && ret.render(false, ''); // render blank..
39974             this.view = ret;
39975             return ret;
39976         }
39977         return false;
39978     }
39979     \*/
39980 });
39981  
39982 /**
39983  * @class Roo.bootstrap.panel.Grid
39984  * @extends Roo.bootstrap.panel.Content
39985  * @constructor
39986  * Create a new GridPanel.
39987  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39988  * @param {Object} config A the config object
39989   
39990  */
39991
39992
39993
39994 Roo.bootstrap.panel.Grid = function(config)
39995 {
39996     
39997       
39998     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39999         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40000
40001     config.el = this.wrapper;
40002     //this.el = this.wrapper;
40003     
40004       if (config.container) {
40005         // ctor'ed from a Border/panel.grid
40006         
40007         
40008         this.wrapper.setStyle("overflow", "hidden");
40009         this.wrapper.addClass('roo-grid-container');
40010
40011     }
40012     
40013     
40014     if(config.toolbar){
40015         var tool_el = this.wrapper.createChild();    
40016         this.toolbar = Roo.factory(config.toolbar);
40017         var ti = [];
40018         if (config.toolbar.items) {
40019             ti = config.toolbar.items ;
40020             delete config.toolbar.items ;
40021         }
40022         
40023         var nitems = [];
40024         this.toolbar.render(tool_el);
40025         for(var i =0;i < ti.length;i++) {
40026           //  Roo.log(['add child', items[i]]);
40027             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40028         }
40029         this.toolbar.items = nitems;
40030         
40031         delete config.toolbar;
40032     }
40033     
40034     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40035     config.grid.scrollBody = true;;
40036     config.grid.monitorWindowResize = false; // turn off autosizing
40037     config.grid.autoHeight = false;
40038     config.grid.autoWidth = false;
40039     
40040     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40041     
40042     if (config.background) {
40043         // render grid on panel activation (if panel background)
40044         this.on('activate', function(gp) {
40045             if (!gp.grid.rendered) {
40046                 gp.grid.render(this.wrapper);
40047                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40048             }
40049         });
40050             
40051     } else {
40052         this.grid.render(this.wrapper);
40053         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40054
40055     }
40056     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40057     // ??? needed ??? config.el = this.wrapper;
40058     
40059     
40060     
40061   
40062     // xtype created footer. - not sure if will work as we normally have to render first..
40063     if (this.footer && !this.footer.el && this.footer.xtype) {
40064         
40065         var ctr = this.grid.getView().getFooterPanel(true);
40066         this.footer.dataSource = this.grid.dataSource;
40067         this.footer = Roo.factory(this.footer, Roo);
40068         this.footer.render(ctr);
40069         
40070     }
40071     
40072     
40073     
40074     
40075      
40076 };
40077
40078 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40079     getId : function(){
40080         return this.grid.id;
40081     },
40082     
40083     /**
40084      * Returns the grid for this panel
40085      * @return {Roo.bootstrap.Table} 
40086      */
40087     getGrid : function(){
40088         return this.grid;    
40089     },
40090     
40091     setSize : function(width, height){
40092         if(!this.ignoreResize(width, height)){
40093             var grid = this.grid;
40094             var size = this.adjustForComponents(width, height);
40095             // tfoot is not a footer?
40096           
40097             
40098             var gridel = grid.getGridEl();
40099             gridel.setSize(size.width, size.height);
40100             
40101             var tbd = grid.getGridEl().select('tbody', true).first();
40102             var thd = grid.getGridEl().select('thead',true).first();
40103             var tbf= grid.getGridEl().select('tfoot', true).first();
40104
40105             if (tbf) {
40106                 size.height -= thd.getHeight();
40107             }
40108             if (thd) {
40109                 size.height -= thd.getHeight();
40110             }
40111             
40112             tbd.setSize(size.width, size.height );
40113             // this is for the account management tab -seems to work there.
40114             var thd = grid.getGridEl().select('thead',true).first();
40115             //if (tbd) {
40116             //    tbd.setSize(size.width, size.height - thd.getHeight());
40117             //}
40118              
40119             grid.autoSize();
40120         }
40121     },
40122      
40123     
40124     
40125     beforeSlide : function(){
40126         this.grid.getView().scroller.clip();
40127     },
40128     
40129     afterSlide : function(){
40130         this.grid.getView().scroller.unclip();
40131     },
40132     
40133     destroy : function(){
40134         this.grid.destroy();
40135         delete this.grid;
40136         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40137     }
40138 });
40139
40140 /**
40141  * @class Roo.bootstrap.panel.Nest
40142  * @extends Roo.bootstrap.panel.Content
40143  * @constructor
40144  * Create a new Panel, that can contain a layout.Border.
40145  * 
40146  * 
40147  * @param {Roo.BorderLayout} layout The layout for this panel
40148  * @param {String/Object} config A string to set only the title or a config object
40149  */
40150 Roo.bootstrap.panel.Nest = function(config)
40151 {
40152     // construct with only one argument..
40153     /* FIXME - implement nicer consturctors
40154     if (layout.layout) {
40155         config = layout;
40156         layout = config.layout;
40157         delete config.layout;
40158     }
40159     if (layout.xtype && !layout.getEl) {
40160         // then layout needs constructing..
40161         layout = Roo.factory(layout, Roo);
40162     }
40163     */
40164     
40165     config.el =  config.layout.getEl();
40166     
40167     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40168     
40169     config.layout.monitorWindowResize = false; // turn off autosizing
40170     this.layout = config.layout;
40171     this.layout.getEl().addClass("roo-layout-nested-layout");
40172     this.layout.parent = this;
40173     
40174     
40175     
40176     
40177 };
40178
40179 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40180
40181     setSize : function(width, height){
40182         if(!this.ignoreResize(width, height)){
40183             var size = this.adjustForComponents(width, height);
40184             var el = this.layout.getEl();
40185             if (size.height < 1) {
40186                 el.setWidth(size.width);   
40187             } else {
40188                 el.setSize(size.width, size.height);
40189             }
40190             var touch = el.dom.offsetWidth;
40191             this.layout.layout();
40192             // ie requires a double layout on the first pass
40193             if(Roo.isIE && !this.initialized){
40194                 this.initialized = true;
40195                 this.layout.layout();
40196             }
40197         }
40198     },
40199     
40200     // activate all subpanels if not currently active..
40201     
40202     setActiveState : function(active){
40203         this.active = active;
40204         this.setActiveClass(active);
40205         
40206         if(!active){
40207             this.fireEvent("deactivate", this);
40208             return;
40209         }
40210         
40211         this.fireEvent("activate", this);
40212         // not sure if this should happen before or after..
40213         if (!this.layout) {
40214             return; // should not happen..
40215         }
40216         var reg = false;
40217         for (var r in this.layout.regions) {
40218             reg = this.layout.getRegion(r);
40219             if (reg.getActivePanel()) {
40220                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40221                 reg.setActivePanel(reg.getActivePanel());
40222                 continue;
40223             }
40224             if (!reg.panels.length) {
40225                 continue;
40226             }
40227             reg.showPanel(reg.getPanel(0));
40228         }
40229         
40230         
40231         
40232         
40233     },
40234     
40235     /**
40236      * Returns the nested BorderLayout for this panel
40237      * @return {Roo.BorderLayout} 
40238      */
40239     getLayout : function(){
40240         return this.layout;
40241     },
40242     
40243      /**
40244      * Adds a xtype elements to the layout of the nested panel
40245      * <pre><code>
40246
40247 panel.addxtype({
40248        xtype : 'ContentPanel',
40249        region: 'west',
40250        items: [ .... ]
40251    }
40252 );
40253
40254 panel.addxtype({
40255         xtype : 'NestedLayoutPanel',
40256         region: 'west',
40257         layout: {
40258            center: { },
40259            west: { }   
40260         },
40261         items : [ ... list of content panels or nested layout panels.. ]
40262    }
40263 );
40264 </code></pre>
40265      * @param {Object} cfg Xtype definition of item to add.
40266      */
40267     addxtype : function(cfg) {
40268         return this.layout.addxtype(cfg);
40269     
40270     }
40271 });/*
40272  * Based on:
40273  * Ext JS Library 1.1.1
40274  * Copyright(c) 2006-2007, Ext JS, LLC.
40275  *
40276  * Originally Released Under LGPL - original licence link has changed is not relivant.
40277  *
40278  * Fork - LGPL
40279  * <script type="text/javascript">
40280  */
40281 /**
40282  * @class Roo.TabPanel
40283  * @extends Roo.util.Observable
40284  * A lightweight tab container.
40285  * <br><br>
40286  * Usage:
40287  * <pre><code>
40288 // basic tabs 1, built from existing content
40289 var tabs = new Roo.TabPanel("tabs1");
40290 tabs.addTab("script", "View Script");
40291 tabs.addTab("markup", "View Markup");
40292 tabs.activate("script");
40293
40294 // more advanced tabs, built from javascript
40295 var jtabs = new Roo.TabPanel("jtabs");
40296 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40297
40298 // set up the UpdateManager
40299 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40300 var updater = tab2.getUpdateManager();
40301 updater.setDefaultUrl("ajax1.htm");
40302 tab2.on('activate', updater.refresh, updater, true);
40303
40304 // Use setUrl for Ajax loading
40305 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40306 tab3.setUrl("ajax2.htm", null, true);
40307
40308 // Disabled tab
40309 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40310 tab4.disable();
40311
40312 jtabs.activate("jtabs-1");
40313  * </code></pre>
40314  * @constructor
40315  * Create a new TabPanel.
40316  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40317  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40318  */
40319 Roo.bootstrap.panel.Tabs = function(config){
40320     /**
40321     * The container element for this TabPanel.
40322     * @type Roo.Element
40323     */
40324     this.el = Roo.get(config.el);
40325     delete config.el;
40326     if(config){
40327         if(typeof config == "boolean"){
40328             this.tabPosition = config ? "bottom" : "top";
40329         }else{
40330             Roo.apply(this, config);
40331         }
40332     }
40333     
40334     if(this.tabPosition == "bottom"){
40335         // if tabs are at the bottom = create the body first.
40336         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40337         this.el.addClass("roo-tabs-bottom");
40338     }
40339     // next create the tabs holders
40340     
40341     if (this.tabPosition == "west"){
40342         
40343         var reg = this.region; // fake it..
40344         while (reg) {
40345             if (!reg.mgr.parent) {
40346                 break;
40347             }
40348             reg = reg.mgr.parent.region;
40349         }
40350         Roo.log("got nest?");
40351         Roo.log(reg);
40352         if (reg.mgr.getRegion('west')) {
40353             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40354             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40355             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40356             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40357             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40358         
40359             
40360         }
40361         
40362         
40363     } else {
40364      
40365         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40366         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40367         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40368         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40369     }
40370     
40371     
40372     if(Roo.isIE){
40373         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40374     }
40375     
40376     // finally - if tabs are at the top, then create the body last..
40377     if(this.tabPosition != "bottom"){
40378         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40379          * @type Roo.Element
40380          */
40381         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40382         this.el.addClass("roo-tabs-top");
40383     }
40384     this.items = [];
40385
40386     this.bodyEl.setStyle("position", "relative");
40387
40388     this.active = null;
40389     this.activateDelegate = this.activate.createDelegate(this);
40390
40391     this.addEvents({
40392         /**
40393          * @event tabchange
40394          * Fires when the active tab changes
40395          * @param {Roo.TabPanel} this
40396          * @param {Roo.TabPanelItem} activePanel The new active tab
40397          */
40398         "tabchange": true,
40399         /**
40400          * @event beforetabchange
40401          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40402          * @param {Roo.TabPanel} this
40403          * @param {Object} e Set cancel to true on this object to cancel the tab change
40404          * @param {Roo.TabPanelItem} tab The tab being changed to
40405          */
40406         "beforetabchange" : true
40407     });
40408
40409     Roo.EventManager.onWindowResize(this.onResize, this);
40410     this.cpad = this.el.getPadding("lr");
40411     this.hiddenCount = 0;
40412
40413
40414     // toolbar on the tabbar support...
40415     if (this.toolbar) {
40416         alert("no toolbar support yet");
40417         this.toolbar  = false;
40418         /*
40419         var tcfg = this.toolbar;
40420         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40421         this.toolbar = new Roo.Toolbar(tcfg);
40422         if (Roo.isSafari) {
40423             var tbl = tcfg.container.child('table', true);
40424             tbl.setAttribute('width', '100%');
40425         }
40426         */
40427         
40428     }
40429    
40430
40431
40432     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40433 };
40434
40435 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40436     /*
40437      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40438      */
40439     tabPosition : "top",
40440     /*
40441      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40442      */
40443     currentTabWidth : 0,
40444     /*
40445      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40446      */
40447     minTabWidth : 40,
40448     /*
40449      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40450      */
40451     maxTabWidth : 250,
40452     /*
40453      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40454      */
40455     preferredTabWidth : 175,
40456     /*
40457      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40458      */
40459     resizeTabs : false,
40460     /*
40461      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40462      */
40463     monitorResize : true,
40464     /*
40465      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40466      */
40467     toolbar : false,  // set by caller..
40468     
40469     region : false, /// set by caller
40470     
40471     disableTooltips : true, // not used yet...
40472
40473     /**
40474      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40475      * @param {String} id The id of the div to use <b>or create</b>
40476      * @param {String} text The text for the tab
40477      * @param {String} content (optional) Content to put in the TabPanelItem body
40478      * @param {Boolean} closable (optional) True to create a close icon on the tab
40479      * @return {Roo.TabPanelItem} The created TabPanelItem
40480      */
40481     addTab : function(id, text, content, closable, tpl)
40482     {
40483         var item = new Roo.bootstrap.panel.TabItem({
40484             panel: this,
40485             id : id,
40486             text : text,
40487             closable : closable,
40488             tpl : tpl
40489         });
40490         this.addTabItem(item);
40491         if(content){
40492             item.setContent(content);
40493         }
40494         return item;
40495     },
40496
40497     /**
40498      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40499      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40500      * @return {Roo.TabPanelItem}
40501      */
40502     getTab : function(id){
40503         return this.items[id];
40504     },
40505
40506     /**
40507      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40508      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40509      */
40510     hideTab : function(id){
40511         var t = this.items[id];
40512         if(!t.isHidden()){
40513            t.setHidden(true);
40514            this.hiddenCount++;
40515            this.autoSizeTabs();
40516         }
40517     },
40518
40519     /**
40520      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40521      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40522      */
40523     unhideTab : function(id){
40524         var t = this.items[id];
40525         if(t.isHidden()){
40526            t.setHidden(false);
40527            this.hiddenCount--;
40528            this.autoSizeTabs();
40529         }
40530     },
40531
40532     /**
40533      * Adds an existing {@link Roo.TabPanelItem}.
40534      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40535      */
40536     addTabItem : function(item)
40537     {
40538         this.items[item.id] = item;
40539         this.items.push(item);
40540         this.autoSizeTabs();
40541       //  if(this.resizeTabs){
40542     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40543   //         this.autoSizeTabs();
40544 //        }else{
40545 //            item.autoSize();
40546        // }
40547     },
40548
40549     /**
40550      * Removes a {@link Roo.TabPanelItem}.
40551      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40552      */
40553     removeTab : function(id){
40554         var items = this.items;
40555         var tab = items[id];
40556         if(!tab) { return; }
40557         var index = items.indexOf(tab);
40558         if(this.active == tab && items.length > 1){
40559             var newTab = this.getNextAvailable(index);
40560             if(newTab) {
40561                 newTab.activate();
40562             }
40563         }
40564         this.stripEl.dom.removeChild(tab.pnode.dom);
40565         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40566             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40567         }
40568         items.splice(index, 1);
40569         delete this.items[tab.id];
40570         tab.fireEvent("close", tab);
40571         tab.purgeListeners();
40572         this.autoSizeTabs();
40573     },
40574
40575     getNextAvailable : function(start){
40576         var items = this.items;
40577         var index = start;
40578         // look for a next tab that will slide over to
40579         // replace the one being removed
40580         while(index < items.length){
40581             var item = items[++index];
40582             if(item && !item.isHidden()){
40583                 return item;
40584             }
40585         }
40586         // if one isn't found select the previous tab (on the left)
40587         index = start;
40588         while(index >= 0){
40589             var item = items[--index];
40590             if(item && !item.isHidden()){
40591                 return item;
40592             }
40593         }
40594         return null;
40595     },
40596
40597     /**
40598      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40599      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40600      */
40601     disableTab : function(id){
40602         var tab = this.items[id];
40603         if(tab && this.active != tab){
40604             tab.disable();
40605         }
40606     },
40607
40608     /**
40609      * Enables a {@link Roo.TabPanelItem} that is disabled.
40610      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40611      */
40612     enableTab : function(id){
40613         var tab = this.items[id];
40614         tab.enable();
40615     },
40616
40617     /**
40618      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40619      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40620      * @return {Roo.TabPanelItem} The TabPanelItem.
40621      */
40622     activate : function(id)
40623     {
40624         //Roo.log('activite:'  + id);
40625         
40626         var tab = this.items[id];
40627         if(!tab){
40628             return null;
40629         }
40630         if(tab == this.active || tab.disabled){
40631             return tab;
40632         }
40633         var e = {};
40634         this.fireEvent("beforetabchange", this, e, tab);
40635         if(e.cancel !== true && !tab.disabled){
40636             if(this.active){
40637                 this.active.hide();
40638             }
40639             this.active = this.items[id];
40640             this.active.show();
40641             this.fireEvent("tabchange", this, this.active);
40642         }
40643         return tab;
40644     },
40645
40646     /**
40647      * Gets the active {@link Roo.TabPanelItem}.
40648      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40649      */
40650     getActiveTab : function(){
40651         return this.active;
40652     },
40653
40654     /**
40655      * Updates the tab body element to fit the height of the container element
40656      * for overflow scrolling
40657      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40658      */
40659     syncHeight : function(targetHeight){
40660         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40661         var bm = this.bodyEl.getMargins();
40662         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40663         this.bodyEl.setHeight(newHeight);
40664         return newHeight;
40665     },
40666
40667     onResize : function(){
40668         if(this.monitorResize){
40669             this.autoSizeTabs();
40670         }
40671     },
40672
40673     /**
40674      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40675      */
40676     beginUpdate : function(){
40677         this.updating = true;
40678     },
40679
40680     /**
40681      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40682      */
40683     endUpdate : function(){
40684         this.updating = false;
40685         this.autoSizeTabs();
40686     },
40687
40688     /**
40689      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40690      */
40691     autoSizeTabs : function()
40692     {
40693         var count = this.items.length;
40694         var vcount = count - this.hiddenCount;
40695         
40696         if (vcount < 2) {
40697             this.stripEl.hide();
40698         } else {
40699             this.stripEl.show();
40700         }
40701         
40702         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40703             return;
40704         }
40705         
40706         
40707         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40708         var availWidth = Math.floor(w / vcount);
40709         var b = this.stripBody;
40710         if(b.getWidth() > w){
40711             var tabs = this.items;
40712             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40713             if(availWidth < this.minTabWidth){
40714                 /*if(!this.sleft){    // incomplete scrolling code
40715                     this.createScrollButtons();
40716                 }
40717                 this.showScroll();
40718                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40719             }
40720         }else{
40721             if(this.currentTabWidth < this.preferredTabWidth){
40722                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40723             }
40724         }
40725     },
40726
40727     /**
40728      * Returns the number of tabs in this TabPanel.
40729      * @return {Number}
40730      */
40731      getCount : function(){
40732          return this.items.length;
40733      },
40734
40735     /**
40736      * Resizes all the tabs to the passed width
40737      * @param {Number} The new width
40738      */
40739     setTabWidth : function(width){
40740         this.currentTabWidth = width;
40741         for(var i = 0, len = this.items.length; i < len; i++) {
40742                 if(!this.items[i].isHidden()) {
40743                 this.items[i].setWidth(width);
40744             }
40745         }
40746     },
40747
40748     /**
40749      * Destroys this TabPanel
40750      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40751      */
40752     destroy : function(removeEl){
40753         Roo.EventManager.removeResizeListener(this.onResize, this);
40754         for(var i = 0, len = this.items.length; i < len; i++){
40755             this.items[i].purgeListeners();
40756         }
40757         if(removeEl === true){
40758             this.el.update("");
40759             this.el.remove();
40760         }
40761     },
40762     
40763     createStrip : function(container)
40764     {
40765         var strip = document.createElement("nav");
40766         strip.className = Roo.bootstrap.version == 4 ?
40767             "navbar-light bg-light" : 
40768             "navbar navbar-default"; //"x-tabs-wrap";
40769         container.appendChild(strip);
40770         return strip;
40771     },
40772     
40773     createStripList : function(strip)
40774     {
40775         // div wrapper for retard IE
40776         // returns the "tr" element.
40777         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40778         //'<div class="x-tabs-strip-wrap">'+
40779           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40780           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40781         return strip.firstChild; //.firstChild.firstChild.firstChild;
40782     },
40783     createBody : function(container)
40784     {
40785         var body = document.createElement("div");
40786         Roo.id(body, "tab-body");
40787         //Roo.fly(body).addClass("x-tabs-body");
40788         Roo.fly(body).addClass("tab-content");
40789         container.appendChild(body);
40790         return body;
40791     },
40792     createItemBody :function(bodyEl, id){
40793         var body = Roo.getDom(id);
40794         if(!body){
40795             body = document.createElement("div");
40796             body.id = id;
40797         }
40798         //Roo.fly(body).addClass("x-tabs-item-body");
40799         Roo.fly(body).addClass("tab-pane");
40800          bodyEl.insertBefore(body, bodyEl.firstChild);
40801         return body;
40802     },
40803     /** @private */
40804     createStripElements :  function(stripEl, text, closable, tpl)
40805     {
40806         var td = document.createElement("li"); // was td..
40807         td.className = 'nav-item';
40808         
40809         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40810         
40811         
40812         stripEl.appendChild(td);
40813         /*if(closable){
40814             td.className = "x-tabs-closable";
40815             if(!this.closeTpl){
40816                 this.closeTpl = new Roo.Template(
40817                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40818                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40819                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40820                 );
40821             }
40822             var el = this.closeTpl.overwrite(td, {"text": text});
40823             var close = el.getElementsByTagName("div")[0];
40824             var inner = el.getElementsByTagName("em")[0];
40825             return {"el": el, "close": close, "inner": inner};
40826         } else {
40827         */
40828         // not sure what this is..
40829 //            if(!this.tabTpl){
40830                 //this.tabTpl = new Roo.Template(
40831                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40832                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40833                 //);
40834 //                this.tabTpl = new Roo.Template(
40835 //                   '<a href="#">' +
40836 //                   '<span unselectable="on"' +
40837 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40838 //                            ' >{text}</span></a>'
40839 //                );
40840 //                
40841 //            }
40842
40843
40844             var template = tpl || this.tabTpl || false;
40845             
40846             if(!template){
40847                 template =  new Roo.Template(
40848                         Roo.bootstrap.version == 4 ? 
40849                             (
40850                                 '<a class="nav-link" href="#" unselectable="on"' +
40851                                      (this.disableTooltips ? '' : ' title="{text}"') +
40852                                      ' >{text}</a>'
40853                             ) : (
40854                                 '<a class="nav-link" href="#">' +
40855                                 '<span unselectable="on"' +
40856                                          (this.disableTooltips ? '' : ' title="{text}"') +
40857                                     ' >{text}</span></a>'
40858                             )
40859                 );
40860             }
40861             
40862             switch (typeof(template)) {
40863                 case 'object' :
40864                     break;
40865                 case 'string' :
40866                     template = new Roo.Template(template);
40867                     break;
40868                 default :
40869                     break;
40870             }
40871             
40872             var el = template.overwrite(td, {"text": text});
40873             
40874             var inner = el.getElementsByTagName("span")[0];
40875             
40876             return {"el": el, "inner": inner};
40877             
40878     }
40879         
40880     
40881 });
40882
40883 /**
40884  * @class Roo.TabPanelItem
40885  * @extends Roo.util.Observable
40886  * Represents an individual item (tab plus body) in a TabPanel.
40887  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40888  * @param {String} id The id of this TabPanelItem
40889  * @param {String} text The text for the tab of this TabPanelItem
40890  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40891  */
40892 Roo.bootstrap.panel.TabItem = function(config){
40893     /**
40894      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40895      * @type Roo.TabPanel
40896      */
40897     this.tabPanel = config.panel;
40898     /**
40899      * The id for this TabPanelItem
40900      * @type String
40901      */
40902     this.id = config.id;
40903     /** @private */
40904     this.disabled = false;
40905     /** @private */
40906     this.text = config.text;
40907     /** @private */
40908     this.loaded = false;
40909     this.closable = config.closable;
40910
40911     /**
40912      * The body element for this TabPanelItem.
40913      * @type Roo.Element
40914      */
40915     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40916     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40917     this.bodyEl.setStyle("display", "block");
40918     this.bodyEl.setStyle("zoom", "1");
40919     //this.hideAction();
40920
40921     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40922     /** @private */
40923     this.el = Roo.get(els.el);
40924     this.inner = Roo.get(els.inner, true);
40925      this.textEl = Roo.bootstrap.version == 4 ?
40926         this.el : Roo.get(this.el.dom.firstChild, true);
40927
40928     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40929     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40930
40931     
40932 //    this.el.on("mousedown", this.onTabMouseDown, this);
40933     this.el.on("click", this.onTabClick, this);
40934     /** @private */
40935     if(config.closable){
40936         var c = Roo.get(els.close, true);
40937         c.dom.title = this.closeText;
40938         c.addClassOnOver("close-over");
40939         c.on("click", this.closeClick, this);
40940      }
40941
40942     this.addEvents({
40943          /**
40944          * @event activate
40945          * Fires when this tab becomes the active tab.
40946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40947          * @param {Roo.TabPanelItem} this
40948          */
40949         "activate": true,
40950         /**
40951          * @event beforeclose
40952          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40953          * @param {Roo.TabPanelItem} this
40954          * @param {Object} e Set cancel to true on this object to cancel the close.
40955          */
40956         "beforeclose": true,
40957         /**
40958          * @event close
40959          * Fires when this tab is closed.
40960          * @param {Roo.TabPanelItem} this
40961          */
40962          "close": true,
40963         /**
40964          * @event deactivate
40965          * Fires when this tab is no longer the active tab.
40966          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40967          * @param {Roo.TabPanelItem} this
40968          */
40969          "deactivate" : true
40970     });
40971     this.hidden = false;
40972
40973     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40974 };
40975
40976 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40977            {
40978     purgeListeners : function(){
40979        Roo.util.Observable.prototype.purgeListeners.call(this);
40980        this.el.removeAllListeners();
40981     },
40982     /**
40983      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40984      */
40985     show : function(){
40986         this.status_node.addClass("active");
40987         this.showAction();
40988         if(Roo.isOpera){
40989             this.tabPanel.stripWrap.repaint();
40990         }
40991         this.fireEvent("activate", this.tabPanel, this);
40992     },
40993
40994     /**
40995      * Returns true if this tab is the active tab.
40996      * @return {Boolean}
40997      */
40998     isActive : function(){
40999         return this.tabPanel.getActiveTab() == this;
41000     },
41001
41002     /**
41003      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41004      */
41005     hide : function(){
41006         this.status_node.removeClass("active");
41007         this.hideAction();
41008         this.fireEvent("deactivate", this.tabPanel, this);
41009     },
41010
41011     hideAction : function(){
41012         this.bodyEl.hide();
41013         this.bodyEl.setStyle("position", "absolute");
41014         this.bodyEl.setLeft("-20000px");
41015         this.bodyEl.setTop("-20000px");
41016     },
41017
41018     showAction : function(){
41019         this.bodyEl.setStyle("position", "relative");
41020         this.bodyEl.setTop("");
41021         this.bodyEl.setLeft("");
41022         this.bodyEl.show();
41023     },
41024
41025     /**
41026      * Set the tooltip for the tab.
41027      * @param {String} tooltip The tab's tooltip
41028      */
41029     setTooltip : function(text){
41030         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41031             this.textEl.dom.qtip = text;
41032             this.textEl.dom.removeAttribute('title');
41033         }else{
41034             this.textEl.dom.title = text;
41035         }
41036     },
41037
41038     onTabClick : function(e){
41039         e.preventDefault();
41040         this.tabPanel.activate(this.id);
41041     },
41042
41043     onTabMouseDown : function(e){
41044         e.preventDefault();
41045         this.tabPanel.activate(this.id);
41046     },
41047 /*
41048     getWidth : function(){
41049         return this.inner.getWidth();
41050     },
41051
41052     setWidth : function(width){
41053         var iwidth = width - this.linode.getPadding("lr");
41054         this.inner.setWidth(iwidth);
41055         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41056         this.linode.setWidth(width);
41057     },
41058 */
41059     /**
41060      * Show or hide the tab
41061      * @param {Boolean} hidden True to hide or false to show.
41062      */
41063     setHidden : function(hidden){
41064         this.hidden = hidden;
41065         this.linode.setStyle("display", hidden ? "none" : "");
41066     },
41067
41068     /**
41069      * Returns true if this tab is "hidden"
41070      * @return {Boolean}
41071      */
41072     isHidden : function(){
41073         return this.hidden;
41074     },
41075
41076     /**
41077      * Returns the text for this tab
41078      * @return {String}
41079      */
41080     getText : function(){
41081         return this.text;
41082     },
41083     /*
41084     autoSize : function(){
41085         //this.el.beginMeasure();
41086         this.textEl.setWidth(1);
41087         /*
41088          *  #2804 [new] Tabs in Roojs
41089          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41090          */
41091         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41092         //this.el.endMeasure();
41093     //},
41094
41095     /**
41096      * Sets the text for the tab (Note: this also sets the tooltip text)
41097      * @param {String} text The tab's text and tooltip
41098      */
41099     setText : function(text){
41100         this.text = text;
41101         this.textEl.update(text);
41102         this.setTooltip(text);
41103         //if(!this.tabPanel.resizeTabs){
41104         //    this.autoSize();
41105         //}
41106     },
41107     /**
41108      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41109      */
41110     activate : function(){
41111         this.tabPanel.activate(this.id);
41112     },
41113
41114     /**
41115      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41116      */
41117     disable : function(){
41118         if(this.tabPanel.active != this){
41119             this.disabled = true;
41120             this.status_node.addClass("disabled");
41121         }
41122     },
41123
41124     /**
41125      * Enables this TabPanelItem if it was previously disabled.
41126      */
41127     enable : function(){
41128         this.disabled = false;
41129         this.status_node.removeClass("disabled");
41130     },
41131
41132     /**
41133      * Sets the content for this TabPanelItem.
41134      * @param {String} content The content
41135      * @param {Boolean} loadScripts true to look for and load scripts
41136      */
41137     setContent : function(content, loadScripts){
41138         this.bodyEl.update(content, loadScripts);
41139     },
41140
41141     /**
41142      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41143      * @return {Roo.UpdateManager} The UpdateManager
41144      */
41145     getUpdateManager : function(){
41146         return this.bodyEl.getUpdateManager();
41147     },
41148
41149     /**
41150      * Set a URL to be used to load the content for this TabPanelItem.
41151      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41152      * @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)
41153      * @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)
41154      * @return {Roo.UpdateManager} The UpdateManager
41155      */
41156     setUrl : function(url, params, loadOnce){
41157         if(this.refreshDelegate){
41158             this.un('activate', this.refreshDelegate);
41159         }
41160         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41161         this.on("activate", this.refreshDelegate);
41162         return this.bodyEl.getUpdateManager();
41163     },
41164
41165     /** @private */
41166     _handleRefresh : function(url, params, loadOnce){
41167         if(!loadOnce || !this.loaded){
41168             var updater = this.bodyEl.getUpdateManager();
41169             updater.update(url, params, this._setLoaded.createDelegate(this));
41170         }
41171     },
41172
41173     /**
41174      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41175      *   Will fail silently if the setUrl method has not been called.
41176      *   This does not activate the panel, just updates its content.
41177      */
41178     refresh : function(){
41179         if(this.refreshDelegate){
41180            this.loaded = false;
41181            this.refreshDelegate();
41182         }
41183     },
41184
41185     /** @private */
41186     _setLoaded : function(){
41187         this.loaded = true;
41188     },
41189
41190     /** @private */
41191     closeClick : function(e){
41192         var o = {};
41193         e.stopEvent();
41194         this.fireEvent("beforeclose", this, o);
41195         if(o.cancel !== true){
41196             this.tabPanel.removeTab(this.id);
41197         }
41198     },
41199     /**
41200      * The text displayed in the tooltip for the close icon.
41201      * @type String
41202      */
41203     closeText : "Close this tab"
41204 });
41205 /**
41206 *    This script refer to:
41207 *    Title: International Telephone Input
41208 *    Author: Jack O'Connor
41209 *    Code version:  v12.1.12
41210 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41211 **/
41212
41213 Roo.bootstrap.PhoneInputData = function() {
41214     var d = [
41215       [
41216         "Afghanistan (‫افغانستان‬‎)",
41217         "af",
41218         "93"
41219       ],
41220       [
41221         "Albania (Shqipëri)",
41222         "al",
41223         "355"
41224       ],
41225       [
41226         "Algeria (‫الجزائر‬‎)",
41227         "dz",
41228         "213"
41229       ],
41230       [
41231         "American Samoa",
41232         "as",
41233         "1684"
41234       ],
41235       [
41236         "Andorra",
41237         "ad",
41238         "376"
41239       ],
41240       [
41241         "Angola",
41242         "ao",
41243         "244"
41244       ],
41245       [
41246         "Anguilla",
41247         "ai",
41248         "1264"
41249       ],
41250       [
41251         "Antigua and Barbuda",
41252         "ag",
41253         "1268"
41254       ],
41255       [
41256         "Argentina",
41257         "ar",
41258         "54"
41259       ],
41260       [
41261         "Armenia (Հայաստան)",
41262         "am",
41263         "374"
41264       ],
41265       [
41266         "Aruba",
41267         "aw",
41268         "297"
41269       ],
41270       [
41271         "Australia",
41272         "au",
41273         "61",
41274         0
41275       ],
41276       [
41277         "Austria (Österreich)",
41278         "at",
41279         "43"
41280       ],
41281       [
41282         "Azerbaijan (Azərbaycan)",
41283         "az",
41284         "994"
41285       ],
41286       [
41287         "Bahamas",
41288         "bs",
41289         "1242"
41290       ],
41291       [
41292         "Bahrain (‫البحرين‬‎)",
41293         "bh",
41294         "973"
41295       ],
41296       [
41297         "Bangladesh (বাংলাদেশ)",
41298         "bd",
41299         "880"
41300       ],
41301       [
41302         "Barbados",
41303         "bb",
41304         "1246"
41305       ],
41306       [
41307         "Belarus (Беларусь)",
41308         "by",
41309         "375"
41310       ],
41311       [
41312         "Belgium (België)",
41313         "be",
41314         "32"
41315       ],
41316       [
41317         "Belize",
41318         "bz",
41319         "501"
41320       ],
41321       [
41322         "Benin (Bénin)",
41323         "bj",
41324         "229"
41325       ],
41326       [
41327         "Bermuda",
41328         "bm",
41329         "1441"
41330       ],
41331       [
41332         "Bhutan (འབྲུག)",
41333         "bt",
41334         "975"
41335       ],
41336       [
41337         "Bolivia",
41338         "bo",
41339         "591"
41340       ],
41341       [
41342         "Bosnia and Herzegovina (Босна и Херцеговина)",
41343         "ba",
41344         "387"
41345       ],
41346       [
41347         "Botswana",
41348         "bw",
41349         "267"
41350       ],
41351       [
41352         "Brazil (Brasil)",
41353         "br",
41354         "55"
41355       ],
41356       [
41357         "British Indian Ocean Territory",
41358         "io",
41359         "246"
41360       ],
41361       [
41362         "British Virgin Islands",
41363         "vg",
41364         "1284"
41365       ],
41366       [
41367         "Brunei",
41368         "bn",
41369         "673"
41370       ],
41371       [
41372         "Bulgaria (България)",
41373         "bg",
41374         "359"
41375       ],
41376       [
41377         "Burkina Faso",
41378         "bf",
41379         "226"
41380       ],
41381       [
41382         "Burundi (Uburundi)",
41383         "bi",
41384         "257"
41385       ],
41386       [
41387         "Cambodia (កម្ពុជា)",
41388         "kh",
41389         "855"
41390       ],
41391       [
41392         "Cameroon (Cameroun)",
41393         "cm",
41394         "237"
41395       ],
41396       [
41397         "Canada",
41398         "ca",
41399         "1",
41400         1,
41401         ["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"]
41402       ],
41403       [
41404         "Cape Verde (Kabu Verdi)",
41405         "cv",
41406         "238"
41407       ],
41408       [
41409         "Caribbean Netherlands",
41410         "bq",
41411         "599",
41412         1
41413       ],
41414       [
41415         "Cayman Islands",
41416         "ky",
41417         "1345"
41418       ],
41419       [
41420         "Central African Republic (République centrafricaine)",
41421         "cf",
41422         "236"
41423       ],
41424       [
41425         "Chad (Tchad)",
41426         "td",
41427         "235"
41428       ],
41429       [
41430         "Chile",
41431         "cl",
41432         "56"
41433       ],
41434       [
41435         "China (中国)",
41436         "cn",
41437         "86"
41438       ],
41439       [
41440         "Christmas Island",
41441         "cx",
41442         "61",
41443         2
41444       ],
41445       [
41446         "Cocos (Keeling) Islands",
41447         "cc",
41448         "61",
41449         1
41450       ],
41451       [
41452         "Colombia",
41453         "co",
41454         "57"
41455       ],
41456       [
41457         "Comoros (‫جزر القمر‬‎)",
41458         "km",
41459         "269"
41460       ],
41461       [
41462         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41463         "cd",
41464         "243"
41465       ],
41466       [
41467         "Congo (Republic) (Congo-Brazzaville)",
41468         "cg",
41469         "242"
41470       ],
41471       [
41472         "Cook Islands",
41473         "ck",
41474         "682"
41475       ],
41476       [
41477         "Costa Rica",
41478         "cr",
41479         "506"
41480       ],
41481       [
41482         "Côte d’Ivoire",
41483         "ci",
41484         "225"
41485       ],
41486       [
41487         "Croatia (Hrvatska)",
41488         "hr",
41489         "385"
41490       ],
41491       [
41492         "Cuba",
41493         "cu",
41494         "53"
41495       ],
41496       [
41497         "Curaçao",
41498         "cw",
41499         "599",
41500         0
41501       ],
41502       [
41503         "Cyprus (Κύπρος)",
41504         "cy",
41505         "357"
41506       ],
41507       [
41508         "Czech Republic (Česká republika)",
41509         "cz",
41510         "420"
41511       ],
41512       [
41513         "Denmark (Danmark)",
41514         "dk",
41515         "45"
41516       ],
41517       [
41518         "Djibouti",
41519         "dj",
41520         "253"
41521       ],
41522       [
41523         "Dominica",
41524         "dm",
41525         "1767"
41526       ],
41527       [
41528         "Dominican Republic (República Dominicana)",
41529         "do",
41530         "1",
41531         2,
41532         ["809", "829", "849"]
41533       ],
41534       [
41535         "Ecuador",
41536         "ec",
41537         "593"
41538       ],
41539       [
41540         "Egypt (‫مصر‬‎)",
41541         "eg",
41542         "20"
41543       ],
41544       [
41545         "El Salvador",
41546         "sv",
41547         "503"
41548       ],
41549       [
41550         "Equatorial Guinea (Guinea Ecuatorial)",
41551         "gq",
41552         "240"
41553       ],
41554       [
41555         "Eritrea",
41556         "er",
41557         "291"
41558       ],
41559       [
41560         "Estonia (Eesti)",
41561         "ee",
41562         "372"
41563       ],
41564       [
41565         "Ethiopia",
41566         "et",
41567         "251"
41568       ],
41569       [
41570         "Falkland Islands (Islas Malvinas)",
41571         "fk",
41572         "500"
41573       ],
41574       [
41575         "Faroe Islands (Føroyar)",
41576         "fo",
41577         "298"
41578       ],
41579       [
41580         "Fiji",
41581         "fj",
41582         "679"
41583       ],
41584       [
41585         "Finland (Suomi)",
41586         "fi",
41587         "358",
41588         0
41589       ],
41590       [
41591         "France",
41592         "fr",
41593         "33"
41594       ],
41595       [
41596         "French Guiana (Guyane française)",
41597         "gf",
41598         "594"
41599       ],
41600       [
41601         "French Polynesia (Polynésie française)",
41602         "pf",
41603         "689"
41604       ],
41605       [
41606         "Gabon",
41607         "ga",
41608         "241"
41609       ],
41610       [
41611         "Gambia",
41612         "gm",
41613         "220"
41614       ],
41615       [
41616         "Georgia (საქართველო)",
41617         "ge",
41618         "995"
41619       ],
41620       [
41621         "Germany (Deutschland)",
41622         "de",
41623         "49"
41624       ],
41625       [
41626         "Ghana (Gaana)",
41627         "gh",
41628         "233"
41629       ],
41630       [
41631         "Gibraltar",
41632         "gi",
41633         "350"
41634       ],
41635       [
41636         "Greece (Ελλάδα)",
41637         "gr",
41638         "30"
41639       ],
41640       [
41641         "Greenland (Kalaallit Nunaat)",
41642         "gl",
41643         "299"
41644       ],
41645       [
41646         "Grenada",
41647         "gd",
41648         "1473"
41649       ],
41650       [
41651         "Guadeloupe",
41652         "gp",
41653         "590",
41654         0
41655       ],
41656       [
41657         "Guam",
41658         "gu",
41659         "1671"
41660       ],
41661       [
41662         "Guatemala",
41663         "gt",
41664         "502"
41665       ],
41666       [
41667         "Guernsey",
41668         "gg",
41669         "44",
41670         1
41671       ],
41672       [
41673         "Guinea (Guinée)",
41674         "gn",
41675         "224"
41676       ],
41677       [
41678         "Guinea-Bissau (Guiné Bissau)",
41679         "gw",
41680         "245"
41681       ],
41682       [
41683         "Guyana",
41684         "gy",
41685         "592"
41686       ],
41687       [
41688         "Haiti",
41689         "ht",
41690         "509"
41691       ],
41692       [
41693         "Honduras",
41694         "hn",
41695         "504"
41696       ],
41697       [
41698         "Hong Kong (香港)",
41699         "hk",
41700         "852"
41701       ],
41702       [
41703         "Hungary (Magyarország)",
41704         "hu",
41705         "36"
41706       ],
41707       [
41708         "Iceland (Ísland)",
41709         "is",
41710         "354"
41711       ],
41712       [
41713         "India (भारत)",
41714         "in",
41715         "91"
41716       ],
41717       [
41718         "Indonesia",
41719         "id",
41720         "62"
41721       ],
41722       [
41723         "Iran (‫ایران‬‎)",
41724         "ir",
41725         "98"
41726       ],
41727       [
41728         "Iraq (‫العراق‬‎)",
41729         "iq",
41730         "964"
41731       ],
41732       [
41733         "Ireland",
41734         "ie",
41735         "353"
41736       ],
41737       [
41738         "Isle of Man",
41739         "im",
41740         "44",
41741         2
41742       ],
41743       [
41744         "Israel (‫ישראל‬‎)",
41745         "il",
41746         "972"
41747       ],
41748       [
41749         "Italy (Italia)",
41750         "it",
41751         "39",
41752         0
41753       ],
41754       [
41755         "Jamaica",
41756         "jm",
41757         "1876"
41758       ],
41759       [
41760         "Japan (日本)",
41761         "jp",
41762         "81"
41763       ],
41764       [
41765         "Jersey",
41766         "je",
41767         "44",
41768         3
41769       ],
41770       [
41771         "Jordan (‫الأردن‬‎)",
41772         "jo",
41773         "962"
41774       ],
41775       [
41776         "Kazakhstan (Казахстан)",
41777         "kz",
41778         "7",
41779         1
41780       ],
41781       [
41782         "Kenya",
41783         "ke",
41784         "254"
41785       ],
41786       [
41787         "Kiribati",
41788         "ki",
41789         "686"
41790       ],
41791       [
41792         "Kosovo",
41793         "xk",
41794         "383"
41795       ],
41796       [
41797         "Kuwait (‫الكويت‬‎)",
41798         "kw",
41799         "965"
41800       ],
41801       [
41802         "Kyrgyzstan (Кыргызстан)",
41803         "kg",
41804         "996"
41805       ],
41806       [
41807         "Laos (ລາວ)",
41808         "la",
41809         "856"
41810       ],
41811       [
41812         "Latvia (Latvija)",
41813         "lv",
41814         "371"
41815       ],
41816       [
41817         "Lebanon (‫لبنان‬‎)",
41818         "lb",
41819         "961"
41820       ],
41821       [
41822         "Lesotho",
41823         "ls",
41824         "266"
41825       ],
41826       [
41827         "Liberia",
41828         "lr",
41829         "231"
41830       ],
41831       [
41832         "Libya (‫ليبيا‬‎)",
41833         "ly",
41834         "218"
41835       ],
41836       [
41837         "Liechtenstein",
41838         "li",
41839         "423"
41840       ],
41841       [
41842         "Lithuania (Lietuva)",
41843         "lt",
41844         "370"
41845       ],
41846       [
41847         "Luxembourg",
41848         "lu",
41849         "352"
41850       ],
41851       [
41852         "Macau (澳門)",
41853         "mo",
41854         "853"
41855       ],
41856       [
41857         "Macedonia (FYROM) (Македонија)",
41858         "mk",
41859         "389"
41860       ],
41861       [
41862         "Madagascar (Madagasikara)",
41863         "mg",
41864         "261"
41865       ],
41866       [
41867         "Malawi",
41868         "mw",
41869         "265"
41870       ],
41871       [
41872         "Malaysia",
41873         "my",
41874         "60"
41875       ],
41876       [
41877         "Maldives",
41878         "mv",
41879         "960"
41880       ],
41881       [
41882         "Mali",
41883         "ml",
41884         "223"
41885       ],
41886       [
41887         "Malta",
41888         "mt",
41889         "356"
41890       ],
41891       [
41892         "Marshall Islands",
41893         "mh",
41894         "692"
41895       ],
41896       [
41897         "Martinique",
41898         "mq",
41899         "596"
41900       ],
41901       [
41902         "Mauritania (‫موريتانيا‬‎)",
41903         "mr",
41904         "222"
41905       ],
41906       [
41907         "Mauritius (Moris)",
41908         "mu",
41909         "230"
41910       ],
41911       [
41912         "Mayotte",
41913         "yt",
41914         "262",
41915         1
41916       ],
41917       [
41918         "Mexico (México)",
41919         "mx",
41920         "52"
41921       ],
41922       [
41923         "Micronesia",
41924         "fm",
41925         "691"
41926       ],
41927       [
41928         "Moldova (Republica Moldova)",
41929         "md",
41930         "373"
41931       ],
41932       [
41933         "Monaco",
41934         "mc",
41935         "377"
41936       ],
41937       [
41938         "Mongolia (Монгол)",
41939         "mn",
41940         "976"
41941       ],
41942       [
41943         "Montenegro (Crna Gora)",
41944         "me",
41945         "382"
41946       ],
41947       [
41948         "Montserrat",
41949         "ms",
41950         "1664"
41951       ],
41952       [
41953         "Morocco (‫المغرب‬‎)",
41954         "ma",
41955         "212",
41956         0
41957       ],
41958       [
41959         "Mozambique (Moçambique)",
41960         "mz",
41961         "258"
41962       ],
41963       [
41964         "Myanmar (Burma) (မြန်မာ)",
41965         "mm",
41966         "95"
41967       ],
41968       [
41969         "Namibia (Namibië)",
41970         "na",
41971         "264"
41972       ],
41973       [
41974         "Nauru",
41975         "nr",
41976         "674"
41977       ],
41978       [
41979         "Nepal (नेपाल)",
41980         "np",
41981         "977"
41982       ],
41983       [
41984         "Netherlands (Nederland)",
41985         "nl",
41986         "31"
41987       ],
41988       [
41989         "New Caledonia (Nouvelle-Calédonie)",
41990         "nc",
41991         "687"
41992       ],
41993       [
41994         "New Zealand",
41995         "nz",
41996         "64"
41997       ],
41998       [
41999         "Nicaragua",
42000         "ni",
42001         "505"
42002       ],
42003       [
42004         "Niger (Nijar)",
42005         "ne",
42006         "227"
42007       ],
42008       [
42009         "Nigeria",
42010         "ng",
42011         "234"
42012       ],
42013       [
42014         "Niue",
42015         "nu",
42016         "683"
42017       ],
42018       [
42019         "Norfolk Island",
42020         "nf",
42021         "672"
42022       ],
42023       [
42024         "North Korea (조선 민주주의 인민 공화국)",
42025         "kp",
42026         "850"
42027       ],
42028       [
42029         "Northern Mariana Islands",
42030         "mp",
42031         "1670"
42032       ],
42033       [
42034         "Norway (Norge)",
42035         "no",
42036         "47",
42037         0
42038       ],
42039       [
42040         "Oman (‫عُمان‬‎)",
42041         "om",
42042         "968"
42043       ],
42044       [
42045         "Pakistan (‫پاکستان‬‎)",
42046         "pk",
42047         "92"
42048       ],
42049       [
42050         "Palau",
42051         "pw",
42052         "680"
42053       ],
42054       [
42055         "Palestine (‫فلسطين‬‎)",
42056         "ps",
42057         "970"
42058       ],
42059       [
42060         "Panama (Panamá)",
42061         "pa",
42062         "507"
42063       ],
42064       [
42065         "Papua New Guinea",
42066         "pg",
42067         "675"
42068       ],
42069       [
42070         "Paraguay",
42071         "py",
42072         "595"
42073       ],
42074       [
42075         "Peru (Perú)",
42076         "pe",
42077         "51"
42078       ],
42079       [
42080         "Philippines",
42081         "ph",
42082         "63"
42083       ],
42084       [
42085         "Poland (Polska)",
42086         "pl",
42087         "48"
42088       ],
42089       [
42090         "Portugal",
42091         "pt",
42092         "351"
42093       ],
42094       [
42095         "Puerto Rico",
42096         "pr",
42097         "1",
42098         3,
42099         ["787", "939"]
42100       ],
42101       [
42102         "Qatar (‫قطر‬‎)",
42103         "qa",
42104         "974"
42105       ],
42106       [
42107         "Réunion (La Réunion)",
42108         "re",
42109         "262",
42110         0
42111       ],
42112       [
42113         "Romania (România)",
42114         "ro",
42115         "40"
42116       ],
42117       [
42118         "Russia (Россия)",
42119         "ru",
42120         "7",
42121         0
42122       ],
42123       [
42124         "Rwanda",
42125         "rw",
42126         "250"
42127       ],
42128       [
42129         "Saint Barthélemy",
42130         "bl",
42131         "590",
42132         1
42133       ],
42134       [
42135         "Saint Helena",
42136         "sh",
42137         "290"
42138       ],
42139       [
42140         "Saint Kitts and Nevis",
42141         "kn",
42142         "1869"
42143       ],
42144       [
42145         "Saint Lucia",
42146         "lc",
42147         "1758"
42148       ],
42149       [
42150         "Saint Martin (Saint-Martin (partie française))",
42151         "mf",
42152         "590",
42153         2
42154       ],
42155       [
42156         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42157         "pm",
42158         "508"
42159       ],
42160       [
42161         "Saint Vincent and the Grenadines",
42162         "vc",
42163         "1784"
42164       ],
42165       [
42166         "Samoa",
42167         "ws",
42168         "685"
42169       ],
42170       [
42171         "San Marino",
42172         "sm",
42173         "378"
42174       ],
42175       [
42176         "São Tomé and Príncipe (São Tomé e Príncipe)",
42177         "st",
42178         "239"
42179       ],
42180       [
42181         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42182         "sa",
42183         "966"
42184       ],
42185       [
42186         "Senegal (Sénégal)",
42187         "sn",
42188         "221"
42189       ],
42190       [
42191         "Serbia (Србија)",
42192         "rs",
42193         "381"
42194       ],
42195       [
42196         "Seychelles",
42197         "sc",
42198         "248"
42199       ],
42200       [
42201         "Sierra Leone",
42202         "sl",
42203         "232"
42204       ],
42205       [
42206         "Singapore",
42207         "sg",
42208         "65"
42209       ],
42210       [
42211         "Sint Maarten",
42212         "sx",
42213         "1721"
42214       ],
42215       [
42216         "Slovakia (Slovensko)",
42217         "sk",
42218         "421"
42219       ],
42220       [
42221         "Slovenia (Slovenija)",
42222         "si",
42223         "386"
42224       ],
42225       [
42226         "Solomon Islands",
42227         "sb",
42228         "677"
42229       ],
42230       [
42231         "Somalia (Soomaaliya)",
42232         "so",
42233         "252"
42234       ],
42235       [
42236         "South Africa",
42237         "za",
42238         "27"
42239       ],
42240       [
42241         "South Korea (대한민국)",
42242         "kr",
42243         "82"
42244       ],
42245       [
42246         "South Sudan (‫جنوب السودان‬‎)",
42247         "ss",
42248         "211"
42249       ],
42250       [
42251         "Spain (España)",
42252         "es",
42253         "34"
42254       ],
42255       [
42256         "Sri Lanka (ශ්‍රී ලංකාව)",
42257         "lk",
42258         "94"
42259       ],
42260       [
42261         "Sudan (‫السودان‬‎)",
42262         "sd",
42263         "249"
42264       ],
42265       [
42266         "Suriname",
42267         "sr",
42268         "597"
42269       ],
42270       [
42271         "Svalbard and Jan Mayen",
42272         "sj",
42273         "47",
42274         1
42275       ],
42276       [
42277         "Swaziland",
42278         "sz",
42279         "268"
42280       ],
42281       [
42282         "Sweden (Sverige)",
42283         "se",
42284         "46"
42285       ],
42286       [
42287         "Switzerland (Schweiz)",
42288         "ch",
42289         "41"
42290       ],
42291       [
42292         "Syria (‫سوريا‬‎)",
42293         "sy",
42294         "963"
42295       ],
42296       [
42297         "Taiwan (台灣)",
42298         "tw",
42299         "886"
42300       ],
42301       [
42302         "Tajikistan",
42303         "tj",
42304         "992"
42305       ],
42306       [
42307         "Tanzania",
42308         "tz",
42309         "255"
42310       ],
42311       [
42312         "Thailand (ไทย)",
42313         "th",
42314         "66"
42315       ],
42316       [
42317         "Timor-Leste",
42318         "tl",
42319         "670"
42320       ],
42321       [
42322         "Togo",
42323         "tg",
42324         "228"
42325       ],
42326       [
42327         "Tokelau",
42328         "tk",
42329         "690"
42330       ],
42331       [
42332         "Tonga",
42333         "to",
42334         "676"
42335       ],
42336       [
42337         "Trinidad and Tobago",
42338         "tt",
42339         "1868"
42340       ],
42341       [
42342         "Tunisia (‫تونس‬‎)",
42343         "tn",
42344         "216"
42345       ],
42346       [
42347         "Turkey (Türkiye)",
42348         "tr",
42349         "90"
42350       ],
42351       [
42352         "Turkmenistan",
42353         "tm",
42354         "993"
42355       ],
42356       [
42357         "Turks and Caicos Islands",
42358         "tc",
42359         "1649"
42360       ],
42361       [
42362         "Tuvalu",
42363         "tv",
42364         "688"
42365       ],
42366       [
42367         "U.S. Virgin Islands",
42368         "vi",
42369         "1340"
42370       ],
42371       [
42372         "Uganda",
42373         "ug",
42374         "256"
42375       ],
42376       [
42377         "Ukraine (Україна)",
42378         "ua",
42379         "380"
42380       ],
42381       [
42382         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42383         "ae",
42384         "971"
42385       ],
42386       [
42387         "United Kingdom",
42388         "gb",
42389         "44",
42390         0
42391       ],
42392       [
42393         "United States",
42394         "us",
42395         "1",
42396         0
42397       ],
42398       [
42399         "Uruguay",
42400         "uy",
42401         "598"
42402       ],
42403       [
42404         "Uzbekistan (Oʻzbekiston)",
42405         "uz",
42406         "998"
42407       ],
42408       [
42409         "Vanuatu",
42410         "vu",
42411         "678"
42412       ],
42413       [
42414         "Vatican City (Città del Vaticano)",
42415         "va",
42416         "39",
42417         1
42418       ],
42419       [
42420         "Venezuela",
42421         "ve",
42422         "58"
42423       ],
42424       [
42425         "Vietnam (Việt Nam)",
42426         "vn",
42427         "84"
42428       ],
42429       [
42430         "Wallis and Futuna (Wallis-et-Futuna)",
42431         "wf",
42432         "681"
42433       ],
42434       [
42435         "Western Sahara (‫الصحراء الغربية‬‎)",
42436         "eh",
42437         "212",
42438         1
42439       ],
42440       [
42441         "Yemen (‫اليمن‬‎)",
42442         "ye",
42443         "967"
42444       ],
42445       [
42446         "Zambia",
42447         "zm",
42448         "260"
42449       ],
42450       [
42451         "Zimbabwe",
42452         "zw",
42453         "263"
42454       ],
42455       [
42456         "Åland Islands",
42457         "ax",
42458         "358",
42459         1
42460       ]
42461   ];
42462   
42463   return d;
42464 }/**
42465 *    This script refer to:
42466 *    Title: International Telephone Input
42467 *    Author: Jack O'Connor
42468 *    Code version:  v12.1.12
42469 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42470 **/
42471
42472 /**
42473  * @class Roo.bootstrap.PhoneInput
42474  * @extends Roo.bootstrap.TriggerField
42475  * An input with International dial-code selection
42476  
42477  * @cfg {String} defaultDialCode default '+852'
42478  * @cfg {Array} preferedCountries default []
42479   
42480  * @constructor
42481  * Create a new PhoneInput.
42482  * @param {Object} config Configuration options
42483  */
42484
42485 Roo.bootstrap.PhoneInput = function(config) {
42486     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42487 };
42488
42489 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42490         
42491         listWidth: undefined,
42492         
42493         selectedClass: 'active',
42494         
42495         invalidClass : "has-warning",
42496         
42497         validClass: 'has-success',
42498         
42499         allowed: '0123456789',
42500         
42501         max_length: 15,
42502         
42503         /**
42504          * @cfg {String} defaultDialCode The default dial code when initializing the input
42505          */
42506         defaultDialCode: '+852',
42507         
42508         /**
42509          * @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
42510          */
42511         preferedCountries: false,
42512         
42513         getAutoCreate : function()
42514         {
42515             var data = Roo.bootstrap.PhoneInputData();
42516             var align = this.labelAlign || this.parentLabelAlign();
42517             var id = Roo.id();
42518             
42519             this.allCountries = [];
42520             this.dialCodeMapping = [];
42521             
42522             for (var i = 0; i < data.length; i++) {
42523               var c = data[i];
42524               this.allCountries[i] = {
42525                 name: c[0],
42526                 iso2: c[1],
42527                 dialCode: c[2],
42528                 priority: c[3] || 0,
42529                 areaCodes: c[4] || null
42530               };
42531               this.dialCodeMapping[c[2]] = {
42532                   name: c[0],
42533                   iso2: c[1],
42534                   priority: c[3] || 0,
42535                   areaCodes: c[4] || null
42536               };
42537             }
42538             
42539             var cfg = {
42540                 cls: 'form-group',
42541                 cn: []
42542             };
42543             
42544             var input =  {
42545                 tag: 'input',
42546                 id : id,
42547                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42548                 maxlength: this.max_length,
42549                 cls : 'form-control tel-input',
42550                 autocomplete: 'new-password'
42551             };
42552             
42553             var hiddenInput = {
42554                 tag: 'input',
42555                 type: 'hidden',
42556                 cls: 'hidden-tel-input'
42557             };
42558             
42559             if (this.name) {
42560                 hiddenInput.name = this.name;
42561             }
42562             
42563             if (this.disabled) {
42564                 input.disabled = true;
42565             }
42566             
42567             var flag_container = {
42568                 tag: 'div',
42569                 cls: 'flag-box',
42570                 cn: [
42571                     {
42572                         tag: 'div',
42573                         cls: 'flag'
42574                     },
42575                     {
42576                         tag: 'div',
42577                         cls: 'caret'
42578                     }
42579                 ]
42580             };
42581             
42582             var box = {
42583                 tag: 'div',
42584                 cls: this.hasFeedback ? 'has-feedback' : '',
42585                 cn: [
42586                     hiddenInput,
42587                     input,
42588                     {
42589                         tag: 'input',
42590                         cls: 'dial-code-holder',
42591                         disabled: true
42592                     }
42593                 ]
42594             };
42595             
42596             var container = {
42597                 cls: 'roo-select2-container input-group',
42598                 cn: [
42599                     flag_container,
42600                     box
42601                 ]
42602             };
42603             
42604             if (this.fieldLabel.length) {
42605                 var indicator = {
42606                     tag: 'i',
42607                     tooltip: 'This field is required'
42608                 };
42609                 
42610                 var label = {
42611                     tag: 'label',
42612                     'for':  id,
42613                     cls: 'control-label',
42614                     cn: []
42615                 };
42616                 
42617                 var label_text = {
42618                     tag: 'span',
42619                     html: this.fieldLabel
42620                 };
42621                 
42622                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42623                 label.cn = [
42624                     indicator,
42625                     label_text
42626                 ];
42627                 
42628                 if(this.indicatorpos == 'right') {
42629                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42630                     label.cn = [
42631                         label_text,
42632                         indicator
42633                     ];
42634                 }
42635                 
42636                 if(align == 'left') {
42637                     container = {
42638                         tag: 'div',
42639                         cn: [
42640                             container
42641                         ]
42642                     };
42643                     
42644                     if(this.labelWidth > 12){
42645                         label.style = "width: " + this.labelWidth + 'px';
42646                     }
42647                     if(this.labelWidth < 13 && this.labelmd == 0){
42648                         this.labelmd = this.labelWidth;
42649                     }
42650                     if(this.labellg > 0){
42651                         label.cls += ' col-lg-' + this.labellg;
42652                         input.cls += ' col-lg-' + (12 - this.labellg);
42653                     }
42654                     if(this.labelmd > 0){
42655                         label.cls += ' col-md-' + this.labelmd;
42656                         container.cls += ' col-md-' + (12 - this.labelmd);
42657                     }
42658                     if(this.labelsm > 0){
42659                         label.cls += ' col-sm-' + this.labelsm;
42660                         container.cls += ' col-sm-' + (12 - this.labelsm);
42661                     }
42662                     if(this.labelxs > 0){
42663                         label.cls += ' col-xs-' + this.labelxs;
42664                         container.cls += ' col-xs-' + (12 - this.labelxs);
42665                     }
42666                 }
42667             }
42668             
42669             cfg.cn = [
42670                 label,
42671                 container
42672             ];
42673             
42674             var settings = this;
42675             
42676             ['xs','sm','md','lg'].map(function(size){
42677                 if (settings[size]) {
42678                     cfg.cls += ' col-' + size + '-' + settings[size];
42679                 }
42680             });
42681             
42682             this.store = new Roo.data.Store({
42683                 proxy : new Roo.data.MemoryProxy({}),
42684                 reader : new Roo.data.JsonReader({
42685                     fields : [
42686                         {
42687                             'name' : 'name',
42688                             'type' : 'string'
42689                         },
42690                         {
42691                             'name' : 'iso2',
42692                             'type' : 'string'
42693                         },
42694                         {
42695                             'name' : 'dialCode',
42696                             'type' : 'string'
42697                         },
42698                         {
42699                             'name' : 'priority',
42700                             'type' : 'string'
42701                         },
42702                         {
42703                             'name' : 'areaCodes',
42704                             'type' : 'string'
42705                         }
42706                     ]
42707                 })
42708             });
42709             
42710             if(!this.preferedCountries) {
42711                 this.preferedCountries = [
42712                     'hk',
42713                     'gb',
42714                     'us'
42715                 ];
42716             }
42717             
42718             var p = this.preferedCountries.reverse();
42719             
42720             if(p) {
42721                 for (var i = 0; i < p.length; i++) {
42722                     for (var j = 0; j < this.allCountries.length; j++) {
42723                         if(this.allCountries[j].iso2 == p[i]) {
42724                             var t = this.allCountries[j];
42725                             this.allCountries.splice(j,1);
42726                             this.allCountries.unshift(t);
42727                         }
42728                     } 
42729                 }
42730             }
42731             
42732             this.store.proxy.data = {
42733                 success: true,
42734                 data: this.allCountries
42735             };
42736             
42737             return cfg;
42738         },
42739         
42740         initEvents : function()
42741         {
42742             this.createList();
42743             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42744             
42745             this.indicator = this.indicatorEl();
42746             this.flag = this.flagEl();
42747             this.dialCodeHolder = this.dialCodeHolderEl();
42748             
42749             this.trigger = this.el.select('div.flag-box',true).first();
42750             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42751             
42752             var _this = this;
42753             
42754             (function(){
42755                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42756                 _this.list.setWidth(lw);
42757             }).defer(100);
42758             
42759             this.list.on('mouseover', this.onViewOver, this);
42760             this.list.on('mousemove', this.onViewMove, this);
42761             this.inputEl().on("keyup", this.onKeyUp, this);
42762             this.inputEl().on("keypress", this.onKeyPress, this);
42763             
42764             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42765
42766             this.view = new Roo.View(this.list, this.tpl, {
42767                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42768             });
42769             
42770             this.view.on('click', this.onViewClick, this);
42771             this.setValue(this.defaultDialCode);
42772         },
42773         
42774         onTriggerClick : function(e)
42775         {
42776             Roo.log('trigger click');
42777             if(this.disabled){
42778                 return;
42779             }
42780             
42781             if(this.isExpanded()){
42782                 this.collapse();
42783                 this.hasFocus = false;
42784             }else {
42785                 this.store.load({});
42786                 this.hasFocus = true;
42787                 this.expand();
42788             }
42789         },
42790         
42791         isExpanded : function()
42792         {
42793             return this.list.isVisible();
42794         },
42795         
42796         collapse : function()
42797         {
42798             if(!this.isExpanded()){
42799                 return;
42800             }
42801             this.list.hide();
42802             Roo.get(document).un('mousedown', this.collapseIf, this);
42803             Roo.get(document).un('mousewheel', this.collapseIf, this);
42804             this.fireEvent('collapse', this);
42805             this.validate();
42806         },
42807         
42808         expand : function()
42809         {
42810             Roo.log('expand');
42811
42812             if(this.isExpanded() || !this.hasFocus){
42813                 return;
42814             }
42815             
42816             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42817             this.list.setWidth(lw);
42818             
42819             this.list.show();
42820             this.restrictHeight();
42821             
42822             Roo.get(document).on('mousedown', this.collapseIf, this);
42823             Roo.get(document).on('mousewheel', this.collapseIf, this);
42824             
42825             this.fireEvent('expand', this);
42826         },
42827         
42828         restrictHeight : function()
42829         {
42830             this.list.alignTo(this.inputEl(), this.listAlign);
42831             this.list.alignTo(this.inputEl(), this.listAlign);
42832         },
42833         
42834         onViewOver : function(e, t)
42835         {
42836             if(this.inKeyMode){
42837                 return;
42838             }
42839             var item = this.view.findItemFromChild(t);
42840             
42841             if(item){
42842                 var index = this.view.indexOf(item);
42843                 this.select(index, false);
42844             }
42845         },
42846
42847         // private
42848         onViewClick : function(view, doFocus, el, e)
42849         {
42850             var index = this.view.getSelectedIndexes()[0];
42851             
42852             var r = this.store.getAt(index);
42853             
42854             if(r){
42855                 this.onSelect(r, index);
42856             }
42857             if(doFocus !== false && !this.blockFocus){
42858                 this.inputEl().focus();
42859             }
42860         },
42861         
42862         onViewMove : function(e, t)
42863         {
42864             this.inKeyMode = false;
42865         },
42866         
42867         select : function(index, scrollIntoView)
42868         {
42869             this.selectedIndex = index;
42870             this.view.select(index);
42871             if(scrollIntoView !== false){
42872                 var el = this.view.getNode(index);
42873                 if(el){
42874                     this.list.scrollChildIntoView(el, false);
42875                 }
42876             }
42877         },
42878         
42879         createList : function()
42880         {
42881             this.list = Roo.get(document.body).createChild({
42882                 tag: 'ul',
42883                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42884                 style: 'display:none'
42885             });
42886             
42887             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42888         },
42889         
42890         collapseIf : function(e)
42891         {
42892             var in_combo  = e.within(this.el);
42893             var in_list =  e.within(this.list);
42894             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42895             
42896             if (in_combo || in_list || is_list) {
42897                 return;
42898             }
42899             this.collapse();
42900         },
42901         
42902         onSelect : function(record, index)
42903         {
42904             if(this.fireEvent('beforeselect', this, record, index) !== false){
42905                 
42906                 this.setFlagClass(record.data.iso2);
42907                 this.setDialCode(record.data.dialCode);
42908                 this.hasFocus = false;
42909                 this.collapse();
42910                 this.fireEvent('select', this, record, index);
42911             }
42912         },
42913         
42914         flagEl : function()
42915         {
42916             var flag = this.el.select('div.flag',true).first();
42917             if(!flag){
42918                 return false;
42919             }
42920             return flag;
42921         },
42922         
42923         dialCodeHolderEl : function()
42924         {
42925             var d = this.el.select('input.dial-code-holder',true).first();
42926             if(!d){
42927                 return false;
42928             }
42929             return d;
42930         },
42931         
42932         setDialCode : function(v)
42933         {
42934             this.dialCodeHolder.dom.value = '+'+v;
42935         },
42936         
42937         setFlagClass : function(n)
42938         {
42939             this.flag.dom.className = 'flag '+n;
42940         },
42941         
42942         getValue : function()
42943         {
42944             var v = this.inputEl().getValue();
42945             if(this.dialCodeHolder) {
42946                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42947             }
42948             return v;
42949         },
42950         
42951         setValue : function(v)
42952         {
42953             var d = this.getDialCode(v);
42954             
42955             //invalid dial code
42956             if(v.length == 0 || !d || d.length == 0) {
42957                 if(this.rendered){
42958                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42959                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42960                 }
42961                 return;
42962             }
42963             
42964             //valid dial code
42965             this.setFlagClass(this.dialCodeMapping[d].iso2);
42966             this.setDialCode(d);
42967             this.inputEl().dom.value = v.replace('+'+d,'');
42968             this.hiddenEl().dom.value = this.getValue();
42969             
42970             this.validate();
42971         },
42972         
42973         getDialCode : function(v)
42974         {
42975             v = v ||  '';
42976             
42977             if (v.length == 0) {
42978                 return this.dialCodeHolder.dom.value;
42979             }
42980             
42981             var dialCode = "";
42982             if (v.charAt(0) != "+") {
42983                 return false;
42984             }
42985             var numericChars = "";
42986             for (var i = 1; i < v.length; i++) {
42987               var c = v.charAt(i);
42988               if (!isNaN(c)) {
42989                 numericChars += c;
42990                 if (this.dialCodeMapping[numericChars]) {
42991                   dialCode = v.substr(1, i);
42992                 }
42993                 if (numericChars.length == 4) {
42994                   break;
42995                 }
42996               }
42997             }
42998             return dialCode;
42999         },
43000         
43001         reset : function()
43002         {
43003             this.setValue(this.defaultDialCode);
43004             this.validate();
43005         },
43006         
43007         hiddenEl : function()
43008         {
43009             return this.el.select('input.hidden-tel-input',true).first();
43010         },
43011         
43012         // after setting val
43013         onKeyUp : function(e){
43014             this.setValue(this.getValue());
43015         },
43016         
43017         onKeyPress : function(e){
43018             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43019                 e.stopEvent();
43020             }
43021         }
43022         
43023 });
43024 /**
43025  * @class Roo.bootstrap.MoneyField
43026  * @extends Roo.bootstrap.ComboBox
43027  * Bootstrap MoneyField class
43028  * 
43029  * @constructor
43030  * Create a new MoneyField.
43031  * @param {Object} config Configuration options
43032  */
43033
43034 Roo.bootstrap.MoneyField = function(config) {
43035     
43036     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43037     
43038 };
43039
43040 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43041     
43042     /**
43043      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43044      */
43045     allowDecimals : true,
43046     /**
43047      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43048      */
43049     decimalSeparator : ".",
43050     /**
43051      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43052      */
43053     decimalPrecision : 0,
43054     /**
43055      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43056      */
43057     allowNegative : true,
43058     /**
43059      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43060      */
43061     allowZero: true,
43062     /**
43063      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43064      */
43065     minValue : Number.NEGATIVE_INFINITY,
43066     /**
43067      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43068      */
43069     maxValue : Number.MAX_VALUE,
43070     /**
43071      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43072      */
43073     minText : "The minimum value for this field is {0}",
43074     /**
43075      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43076      */
43077     maxText : "The maximum value for this field is {0}",
43078     /**
43079      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43080      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43081      */
43082     nanText : "{0} is not a valid number",
43083     /**
43084      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43085      */
43086     castInt : true,
43087     /**
43088      * @cfg {String} defaults currency of the MoneyField
43089      * value should be in lkey
43090      */
43091     defaultCurrency : false,
43092     /**
43093      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43094      */
43095     thousandsDelimiter : false,
43096     /**
43097      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43098      */
43099     max_length: false,
43100     
43101     inputlg : 9,
43102     inputmd : 9,
43103     inputsm : 9,
43104     inputxs : 6,
43105     
43106     store : false,
43107     
43108     getAutoCreate : function()
43109     {
43110         var align = this.labelAlign || this.parentLabelAlign();
43111         
43112         var id = Roo.id();
43113
43114         var cfg = {
43115             cls: 'form-group',
43116             cn: []
43117         };
43118
43119         var input =  {
43120             tag: 'input',
43121             id : id,
43122             cls : 'form-control roo-money-amount-input',
43123             autocomplete: 'new-password'
43124         };
43125         
43126         var hiddenInput = {
43127             tag: 'input',
43128             type: 'hidden',
43129             id: Roo.id(),
43130             cls: 'hidden-number-input'
43131         };
43132         
43133         if(this.max_length) {
43134             input.maxlength = this.max_length; 
43135         }
43136         
43137         if (this.name) {
43138             hiddenInput.name = this.name;
43139         }
43140
43141         if (this.disabled) {
43142             input.disabled = true;
43143         }
43144
43145         var clg = 12 - this.inputlg;
43146         var cmd = 12 - this.inputmd;
43147         var csm = 12 - this.inputsm;
43148         var cxs = 12 - this.inputxs;
43149         
43150         var container = {
43151             tag : 'div',
43152             cls : 'row roo-money-field',
43153             cn : [
43154                 {
43155                     tag : 'div',
43156                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43157                     cn : [
43158                         {
43159                             tag : 'div',
43160                             cls: 'roo-select2-container input-group',
43161                             cn: [
43162                                 {
43163                                     tag : 'input',
43164                                     cls : 'form-control roo-money-currency-input',
43165                                     autocomplete: 'new-password',
43166                                     readOnly : 1,
43167                                     name : this.currencyName
43168                                 },
43169                                 {
43170                                     tag :'span',
43171                                     cls : 'input-group-addon',
43172                                     cn : [
43173                                         {
43174                                             tag: 'span',
43175                                             cls: 'caret'
43176                                         }
43177                                     ]
43178                                 }
43179                             ]
43180                         }
43181                     ]
43182                 },
43183                 {
43184                     tag : 'div',
43185                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43186                     cn : [
43187                         {
43188                             tag: 'div',
43189                             cls: this.hasFeedback ? 'has-feedback' : '',
43190                             cn: [
43191                                 input
43192                             ]
43193                         }
43194                     ]
43195                 }
43196             ]
43197             
43198         };
43199         
43200         if (this.fieldLabel.length) {
43201             var indicator = {
43202                 tag: 'i',
43203                 tooltip: 'This field is required'
43204             };
43205
43206             var label = {
43207                 tag: 'label',
43208                 'for':  id,
43209                 cls: 'control-label',
43210                 cn: []
43211             };
43212
43213             var label_text = {
43214                 tag: 'span',
43215                 html: this.fieldLabel
43216             };
43217
43218             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43219             label.cn = [
43220                 indicator,
43221                 label_text
43222             ];
43223
43224             if(this.indicatorpos == 'right') {
43225                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43226                 label.cn = [
43227                     label_text,
43228                     indicator
43229                 ];
43230             }
43231
43232             if(align == 'left') {
43233                 container = {
43234                     tag: 'div',
43235                     cn: [
43236                         container
43237                     ]
43238                 };
43239
43240                 if(this.labelWidth > 12){
43241                     label.style = "width: " + this.labelWidth + 'px';
43242                 }
43243                 if(this.labelWidth < 13 && this.labelmd == 0){
43244                     this.labelmd = this.labelWidth;
43245                 }
43246                 if(this.labellg > 0){
43247                     label.cls += ' col-lg-' + this.labellg;
43248                     input.cls += ' col-lg-' + (12 - this.labellg);
43249                 }
43250                 if(this.labelmd > 0){
43251                     label.cls += ' col-md-' + this.labelmd;
43252                     container.cls += ' col-md-' + (12 - this.labelmd);
43253                 }
43254                 if(this.labelsm > 0){
43255                     label.cls += ' col-sm-' + this.labelsm;
43256                     container.cls += ' col-sm-' + (12 - this.labelsm);
43257                 }
43258                 if(this.labelxs > 0){
43259                     label.cls += ' col-xs-' + this.labelxs;
43260                     container.cls += ' col-xs-' + (12 - this.labelxs);
43261                 }
43262             }
43263         }
43264
43265         cfg.cn = [
43266             label,
43267             container,
43268             hiddenInput
43269         ];
43270         
43271         var settings = this;
43272
43273         ['xs','sm','md','lg'].map(function(size){
43274             if (settings[size]) {
43275                 cfg.cls += ' col-' + size + '-' + settings[size];
43276             }
43277         });
43278         
43279         return cfg;
43280     },
43281     
43282     initEvents : function()
43283     {
43284         this.indicator = this.indicatorEl();
43285         
43286         this.initCurrencyEvent();
43287         
43288         this.initNumberEvent();
43289     },
43290     
43291     initCurrencyEvent : function()
43292     {
43293         if (!this.store) {
43294             throw "can not find store for combo";
43295         }
43296         
43297         this.store = Roo.factory(this.store, Roo.data);
43298         this.store.parent = this;
43299         
43300         this.createList();
43301         
43302         this.triggerEl = this.el.select('.input-group-addon', true).first();
43303         
43304         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43305         
43306         var _this = this;
43307         
43308         (function(){
43309             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43310             _this.list.setWidth(lw);
43311         }).defer(100);
43312         
43313         this.list.on('mouseover', this.onViewOver, this);
43314         this.list.on('mousemove', this.onViewMove, this);
43315         this.list.on('scroll', this.onViewScroll, this);
43316         
43317         if(!this.tpl){
43318             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43319         }
43320         
43321         this.view = new Roo.View(this.list, this.tpl, {
43322             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43323         });
43324         
43325         this.view.on('click', this.onViewClick, this);
43326         
43327         this.store.on('beforeload', this.onBeforeLoad, this);
43328         this.store.on('load', this.onLoad, this);
43329         this.store.on('loadexception', this.onLoadException, this);
43330         
43331         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43332             "up" : function(e){
43333                 this.inKeyMode = true;
43334                 this.selectPrev();
43335             },
43336
43337             "down" : function(e){
43338                 if(!this.isExpanded()){
43339                     this.onTriggerClick();
43340                 }else{
43341                     this.inKeyMode = true;
43342                     this.selectNext();
43343                 }
43344             },
43345
43346             "enter" : function(e){
43347                 this.collapse();
43348                 
43349                 if(this.fireEvent("specialkey", this, e)){
43350                     this.onViewClick(false);
43351                 }
43352                 
43353                 return true;
43354             },
43355
43356             "esc" : function(e){
43357                 this.collapse();
43358             },
43359
43360             "tab" : function(e){
43361                 this.collapse();
43362                 
43363                 if(this.fireEvent("specialkey", this, e)){
43364                     this.onViewClick(false);
43365                 }
43366                 
43367                 return true;
43368             },
43369
43370             scope : this,
43371
43372             doRelay : function(foo, bar, hname){
43373                 if(hname == 'down' || this.scope.isExpanded()){
43374                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43375                 }
43376                 return true;
43377             },
43378
43379             forceKeyDown: true
43380         });
43381         
43382         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43383         
43384     },
43385     
43386     initNumberEvent : function(e)
43387     {
43388         this.inputEl().on("keydown" , this.fireKey,  this);
43389         this.inputEl().on("focus", this.onFocus,  this);
43390         this.inputEl().on("blur", this.onBlur,  this);
43391         
43392         this.inputEl().relayEvent('keyup', this);
43393         
43394         if(this.indicator){
43395             this.indicator.addClass('invisible');
43396         }
43397  
43398         this.originalValue = this.getValue();
43399         
43400         if(this.validationEvent == 'keyup'){
43401             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43402             this.inputEl().on('keyup', this.filterValidation, this);
43403         }
43404         else if(this.validationEvent !== false){
43405             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43406         }
43407         
43408         if(this.selectOnFocus){
43409             this.on("focus", this.preFocus, this);
43410             
43411         }
43412         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43413             this.inputEl().on("keypress", this.filterKeys, this);
43414         } else {
43415             this.inputEl().relayEvent('keypress', this);
43416         }
43417         
43418         var allowed = "0123456789";
43419         
43420         if(this.allowDecimals){
43421             allowed += this.decimalSeparator;
43422         }
43423         
43424         if(this.allowNegative){
43425             allowed += "-";
43426         }
43427         
43428         if(this.thousandsDelimiter) {
43429             allowed += ",";
43430         }
43431         
43432         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43433         
43434         var keyPress = function(e){
43435             
43436             var k = e.getKey();
43437             
43438             var c = e.getCharCode();
43439             
43440             if(
43441                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43442                     allowed.indexOf(String.fromCharCode(c)) === -1
43443             ){
43444                 e.stopEvent();
43445                 return;
43446             }
43447             
43448             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43449                 return;
43450             }
43451             
43452             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43453                 e.stopEvent();
43454             }
43455         };
43456         
43457         this.inputEl().on("keypress", keyPress, this);
43458         
43459     },
43460     
43461     onTriggerClick : function(e)
43462     {   
43463         if(this.disabled){
43464             return;
43465         }
43466         
43467         this.page = 0;
43468         this.loadNext = false;
43469         
43470         if(this.isExpanded()){
43471             this.collapse();
43472             return;
43473         }
43474         
43475         this.hasFocus = true;
43476         
43477         if(this.triggerAction == 'all') {
43478             this.doQuery(this.allQuery, true);
43479             return;
43480         }
43481         
43482         this.doQuery(this.getRawValue());
43483     },
43484     
43485     getCurrency : function()
43486     {   
43487         var v = this.currencyEl().getValue();
43488         
43489         return v;
43490     },
43491     
43492     restrictHeight : function()
43493     {
43494         this.list.alignTo(this.currencyEl(), this.listAlign);
43495         this.list.alignTo(this.currencyEl(), this.listAlign);
43496     },
43497     
43498     onViewClick : function(view, doFocus, el, e)
43499     {
43500         var index = this.view.getSelectedIndexes()[0];
43501         
43502         var r = this.store.getAt(index);
43503         
43504         if(r){
43505             this.onSelect(r, index);
43506         }
43507     },
43508     
43509     onSelect : function(record, index){
43510         
43511         if(this.fireEvent('beforeselect', this, record, index) !== false){
43512         
43513             this.setFromCurrencyData(index > -1 ? record.data : false);
43514             
43515             this.collapse();
43516             
43517             this.fireEvent('select', this, record, index);
43518         }
43519     },
43520     
43521     setFromCurrencyData : function(o)
43522     {
43523         var currency = '';
43524         
43525         this.lastCurrency = o;
43526         
43527         if (this.currencyField) {
43528             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43529         } else {
43530             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43531         }
43532         
43533         this.lastSelectionText = currency;
43534         
43535         //setting default currency
43536         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43537             this.setCurrency(this.defaultCurrency);
43538             return;
43539         }
43540         
43541         this.setCurrency(currency);
43542     },
43543     
43544     setFromData : function(o)
43545     {
43546         var c = {};
43547         
43548         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43549         
43550         this.setFromCurrencyData(c);
43551         
43552         var value = '';
43553         
43554         if (this.name) {
43555             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43556         } else {
43557             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43558         }
43559         
43560         this.setValue(value);
43561         
43562     },
43563     
43564     setCurrency : function(v)
43565     {   
43566         this.currencyValue = v;
43567         
43568         if(this.rendered){
43569             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43570             this.validate();
43571         }
43572     },
43573     
43574     setValue : function(v)
43575     {
43576         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43577         
43578         this.value = v;
43579         
43580         if(this.rendered){
43581             
43582             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43583             
43584             this.inputEl().dom.value = (v == '') ? '' :
43585                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43586             
43587             if(!this.allowZero && v === '0') {
43588                 this.hiddenEl().dom.value = '';
43589                 this.inputEl().dom.value = '';
43590             }
43591             
43592             this.validate();
43593         }
43594     },
43595     
43596     getRawValue : function()
43597     {
43598         var v = this.inputEl().getValue();
43599         
43600         return v;
43601     },
43602     
43603     getValue : function()
43604     {
43605         return this.fixPrecision(this.parseValue(this.getRawValue()));
43606     },
43607     
43608     parseValue : function(value)
43609     {
43610         if(this.thousandsDelimiter) {
43611             value += "";
43612             r = new RegExp(",", "g");
43613             value = value.replace(r, "");
43614         }
43615         
43616         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43617         return isNaN(value) ? '' : value;
43618         
43619     },
43620     
43621     fixPrecision : function(value)
43622     {
43623         if(this.thousandsDelimiter) {
43624             value += "";
43625             r = new RegExp(",", "g");
43626             value = value.replace(r, "");
43627         }
43628         
43629         var nan = isNaN(value);
43630         
43631         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43632             return nan ? '' : value;
43633         }
43634         return parseFloat(value).toFixed(this.decimalPrecision);
43635     },
43636     
43637     decimalPrecisionFcn : function(v)
43638     {
43639         return Math.floor(v);
43640     },
43641     
43642     validateValue : function(value)
43643     {
43644         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43645             return false;
43646         }
43647         
43648         var num = this.parseValue(value);
43649         
43650         if(isNaN(num)){
43651             this.markInvalid(String.format(this.nanText, value));
43652             return false;
43653         }
43654         
43655         if(num < this.minValue){
43656             this.markInvalid(String.format(this.minText, this.minValue));
43657             return false;
43658         }
43659         
43660         if(num > this.maxValue){
43661             this.markInvalid(String.format(this.maxText, this.maxValue));
43662             return false;
43663         }
43664         
43665         return true;
43666     },
43667     
43668     validate : function()
43669     {
43670         if(this.disabled || this.allowBlank){
43671             this.markValid();
43672             return true;
43673         }
43674         
43675         var currency = this.getCurrency();
43676         
43677         if(this.validateValue(this.getRawValue()) && currency.length){
43678             this.markValid();
43679             return true;
43680         }
43681         
43682         this.markInvalid();
43683         return false;
43684     },
43685     
43686     getName: function()
43687     {
43688         return this.name;
43689     },
43690     
43691     beforeBlur : function()
43692     {
43693         if(!this.castInt){
43694             return;
43695         }
43696         
43697         var v = this.parseValue(this.getRawValue());
43698         
43699         if(v || v == 0){
43700             this.setValue(v);
43701         }
43702     },
43703     
43704     onBlur : function()
43705     {
43706         this.beforeBlur();
43707         
43708         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43709             //this.el.removeClass(this.focusClass);
43710         }
43711         
43712         this.hasFocus = false;
43713         
43714         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43715             this.validate();
43716         }
43717         
43718         var v = this.getValue();
43719         
43720         if(String(v) !== String(this.startValue)){
43721             this.fireEvent('change', this, v, this.startValue);
43722         }
43723         
43724         this.fireEvent("blur", this);
43725     },
43726     
43727     inputEl : function()
43728     {
43729         return this.el.select('.roo-money-amount-input', true).first();
43730     },
43731     
43732     currencyEl : function()
43733     {
43734         return this.el.select('.roo-money-currency-input', true).first();
43735     },
43736     
43737     hiddenEl : function()
43738     {
43739         return this.el.select('input.hidden-number-input',true).first();
43740     }
43741     
43742 });/**
43743  * @class Roo.bootstrap.BezierSignature
43744  * @extends Roo.bootstrap.Component
43745  * Bootstrap BezierSignature class
43746  * This script refer to:
43747  *    Title: Signature Pad
43748  *    Author: szimek
43749  *    Availability: https://github.com/szimek/signature_pad
43750  *
43751  * @constructor
43752  * Create a new BezierSignature
43753  * @param {Object} config The config object
43754  */
43755
43756 Roo.bootstrap.BezierSignature = function(config){
43757     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43758     this.addEvents({
43759         "resize" : true
43760     });
43761 };
43762
43763 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43764 {
43765      
43766     curve_data: [],
43767     
43768     is_empty: true,
43769     
43770     mouse_btn_down: true,
43771     
43772     /**
43773      * @cfg {int} canvas height
43774      */
43775     canvas_height: '200px',
43776     
43777     /**
43778      * @cfg {float|function} Radius of a single dot.
43779      */ 
43780     dot_size: false,
43781     
43782     /**
43783      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43784      */
43785     min_width: 0.5,
43786     
43787     /**
43788      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43789      */
43790     max_width: 2.5,
43791     
43792     /**
43793      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43794      */
43795     throttle: 16,
43796     
43797     /**
43798      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43799      */
43800     min_distance: 5,
43801     
43802     /**
43803      * @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.
43804      */
43805     bg_color: 'rgba(0, 0, 0, 0)',
43806     
43807     /**
43808      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43809      */
43810     dot_color: 'black',
43811     
43812     /**
43813      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43814      */ 
43815     velocity_filter_weight: 0.7,
43816     
43817     /**
43818      * @cfg {function} Callback when stroke begin. 
43819      */
43820     onBegin: false,
43821     
43822     /**
43823      * @cfg {function} Callback when stroke end.
43824      */
43825     onEnd: false,
43826     
43827     getAutoCreate : function()
43828     {
43829         var cls = 'roo-signature column';
43830         
43831         if(this.cls){
43832             cls += ' ' + this.cls;
43833         }
43834         
43835         var col_sizes = [
43836             'lg',
43837             'md',
43838             'sm',
43839             'xs'
43840         ];
43841         
43842         for(var i = 0; i < col_sizes.length; i++) {
43843             if(this[col_sizes[i]]) {
43844                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43845             }
43846         }
43847         
43848         var cfg = {
43849             tag: 'div',
43850             cls: cls,
43851             cn: [
43852                 {
43853                     tag: 'div',
43854                     cls: 'roo-signature-body',
43855                     cn: [
43856                         {
43857                             tag: 'canvas',
43858                             cls: 'roo-signature-body-canvas',
43859                             height: this.canvas_height,
43860                             width: this.canvas_width
43861                         }
43862                     ]
43863                 },
43864                 {
43865                     tag: 'input',
43866                     type: 'file',
43867                     style: 'display: none'
43868                 }
43869             ]
43870         };
43871         
43872         return cfg;
43873     },
43874     
43875     initEvents: function() 
43876     {
43877         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43878         
43879         var canvas = this.canvasEl();
43880         
43881         // mouse && touch event swapping...
43882         canvas.dom.style.touchAction = 'none';
43883         canvas.dom.style.msTouchAction = 'none';
43884         
43885         this.mouse_btn_down = false;
43886         canvas.on('mousedown', this._handleMouseDown, this);
43887         canvas.on('mousemove', this._handleMouseMove, this);
43888         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43889         
43890         if (window.PointerEvent) {
43891             canvas.on('pointerdown', this._handleMouseDown, this);
43892             canvas.on('pointermove', this._handleMouseMove, this);
43893             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43894         }
43895         
43896         if ('ontouchstart' in window) {
43897             canvas.on('touchstart', this._handleTouchStart, this);
43898             canvas.on('touchmove', this._handleTouchMove, this);
43899             canvas.on('touchend', this._handleTouchEnd, this);
43900         }
43901         
43902         Roo.EventManager.onWindowResize(this.resize, this, true);
43903         
43904         // file input event
43905         this.fileEl().on('change', this.uploadImage, this);
43906         
43907         this.clear();
43908         
43909         this.resize();
43910     },
43911     
43912     resize: function(){
43913         
43914         var canvas = this.canvasEl().dom;
43915         var ctx = this.canvasElCtx();
43916         var img_data = false;
43917         
43918         if(canvas.width > 0) {
43919             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43920         }
43921         // setting canvas width will clean img data
43922         canvas.width = 0;
43923         
43924         var style = window.getComputedStyle ? 
43925             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43926             
43927         var padding_left = parseInt(style.paddingLeft) || 0;
43928         var padding_right = parseInt(style.paddingRight) || 0;
43929         
43930         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43931         
43932         if(img_data) {
43933             ctx.putImageData(img_data, 0, 0);
43934         }
43935     },
43936     
43937     _handleMouseDown: function(e)
43938     {
43939         if (e.browserEvent.which === 1) {
43940             this.mouse_btn_down = true;
43941             this.strokeBegin(e);
43942         }
43943     },
43944     
43945     _handleMouseMove: function (e)
43946     {
43947         if (this.mouse_btn_down) {
43948             this.strokeMoveUpdate(e);
43949         }
43950     },
43951     
43952     _handleMouseUp: function (e)
43953     {
43954         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43955             this.mouse_btn_down = false;
43956             this.strokeEnd(e);
43957         }
43958     },
43959     
43960     _handleTouchStart: function (e) {
43961         
43962         e.preventDefault();
43963         if (e.browserEvent.targetTouches.length === 1) {
43964             // var touch = e.browserEvent.changedTouches[0];
43965             // this.strokeBegin(touch);
43966             
43967              this.strokeBegin(e); // assume e catching the correct xy...
43968         }
43969     },
43970     
43971     _handleTouchMove: function (e) {
43972         e.preventDefault();
43973         // var touch = event.targetTouches[0];
43974         // _this._strokeMoveUpdate(touch);
43975         this.strokeMoveUpdate(e);
43976     },
43977     
43978     _handleTouchEnd: function (e) {
43979         var wasCanvasTouched = e.target === this.canvasEl().dom;
43980         if (wasCanvasTouched) {
43981             e.preventDefault();
43982             // var touch = event.changedTouches[0];
43983             // _this._strokeEnd(touch);
43984             this.strokeEnd(e);
43985         }
43986     },
43987     
43988     reset: function () {
43989         this._lastPoints = [];
43990         this._lastVelocity = 0;
43991         this._lastWidth = (this.min_width + this.max_width) / 2;
43992         this.canvasElCtx().fillStyle = this.dot_color;
43993     },
43994     
43995     strokeMoveUpdate: function(e)
43996     {
43997         this.strokeUpdate(e);
43998         
43999         if (this.throttle) {
44000             this.throttleStroke(this.strokeUpdate, this.throttle);
44001         }
44002         else {
44003             this.strokeUpdate(e);
44004         }
44005     },
44006     
44007     strokeBegin: function(e)
44008     {
44009         var newPointGroup = {
44010             color: this.dot_color,
44011             points: []
44012         };
44013         
44014         if (typeof this.onBegin === 'function') {
44015             this.onBegin(e);
44016         }
44017         
44018         this.curve_data.push(newPointGroup);
44019         this.reset();
44020         this.strokeUpdate(e);
44021     },
44022     
44023     strokeUpdate: function(e)
44024     {
44025         var rect = this.canvasEl().dom.getBoundingClientRect();
44026         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44027         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44028         var lastPoints = lastPointGroup.points;
44029         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44030         var isLastPointTooClose = lastPoint
44031             ? point.distanceTo(lastPoint) <= this.min_distance
44032             : false;
44033         var color = lastPointGroup.color;
44034         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44035             var curve = this.addPoint(point);
44036             if (!lastPoint) {
44037                 this.drawDot({color: color, point: point});
44038             }
44039             else if (curve) {
44040                 this.drawCurve({color: color, curve: curve});
44041             }
44042             lastPoints.push({
44043                 time: point.time,
44044                 x: point.x,
44045                 y: point.y
44046             });
44047         }
44048     },
44049     
44050     strokeEnd: function(e)
44051     {
44052         this.strokeUpdate(e);
44053         if (typeof this.onEnd === 'function') {
44054             this.onEnd(e);
44055         }
44056     },
44057     
44058     addPoint:  function (point) {
44059         var _lastPoints = this._lastPoints;
44060         _lastPoints.push(point);
44061         if (_lastPoints.length > 2) {
44062             if (_lastPoints.length === 3) {
44063                 _lastPoints.unshift(_lastPoints[0]);
44064             }
44065             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44066             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44067             _lastPoints.shift();
44068             return curve;
44069         }
44070         return null;
44071     },
44072     
44073     calculateCurveWidths: function (startPoint, endPoint) {
44074         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44075             (1 - this.velocity_filter_weight) * this._lastVelocity;
44076
44077         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44078         var widths = {
44079             end: newWidth,
44080             start: this._lastWidth
44081         };
44082         
44083         this._lastVelocity = velocity;
44084         this._lastWidth = newWidth;
44085         return widths;
44086     },
44087     
44088     drawDot: function (_a) {
44089         var color = _a.color, point = _a.point;
44090         var ctx = this.canvasElCtx();
44091         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44092         ctx.beginPath();
44093         this.drawCurveSegment(point.x, point.y, width);
44094         ctx.closePath();
44095         ctx.fillStyle = color;
44096         ctx.fill();
44097     },
44098     
44099     drawCurve: function (_a) {
44100         var color = _a.color, curve = _a.curve;
44101         var ctx = this.canvasElCtx();
44102         var widthDelta = curve.endWidth - curve.startWidth;
44103         var drawSteps = Math.floor(curve.length()) * 2;
44104         ctx.beginPath();
44105         ctx.fillStyle = color;
44106         for (var i = 0; i < drawSteps; i += 1) {
44107         var t = i / drawSteps;
44108         var tt = t * t;
44109         var ttt = tt * t;
44110         var u = 1 - t;
44111         var uu = u * u;
44112         var uuu = uu * u;
44113         var x = uuu * curve.startPoint.x;
44114         x += 3 * uu * t * curve.control1.x;
44115         x += 3 * u * tt * curve.control2.x;
44116         x += ttt * curve.endPoint.x;
44117         var y = uuu * curve.startPoint.y;
44118         y += 3 * uu * t * curve.control1.y;
44119         y += 3 * u * tt * curve.control2.y;
44120         y += ttt * curve.endPoint.y;
44121         var width = curve.startWidth + ttt * widthDelta;
44122         this.drawCurveSegment(x, y, width);
44123         }
44124         ctx.closePath();
44125         ctx.fill();
44126     },
44127     
44128     drawCurveSegment: function (x, y, width) {
44129         var ctx = this.canvasElCtx();
44130         ctx.moveTo(x, y);
44131         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44132         this.is_empty = false;
44133     },
44134     
44135     clear: function()
44136     {
44137         var ctx = this.canvasElCtx();
44138         var canvas = this.canvasEl().dom;
44139         ctx.fillStyle = this.bg_color;
44140         ctx.clearRect(0, 0, canvas.width, canvas.height);
44141         ctx.fillRect(0, 0, canvas.width, canvas.height);
44142         this.curve_data = [];
44143         this.reset();
44144         this.is_empty = true;
44145     },
44146     
44147     fileEl: function()
44148     {
44149         return  this.el.select('input',true).first();
44150     },
44151     
44152     canvasEl: function()
44153     {
44154         return this.el.select('canvas',true).first();
44155     },
44156     
44157     canvasElCtx: function()
44158     {
44159         return this.el.select('canvas',true).first().dom.getContext('2d');
44160     },
44161     
44162     getImage: function(type)
44163     {
44164         if(this.is_empty) {
44165             return false;
44166         }
44167         
44168         // encryption ?
44169         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44170     },
44171     
44172     drawFromImage: function(img_src)
44173     {
44174         var img = new Image();
44175         
44176         img.onload = function(){
44177             this.canvasElCtx().drawImage(img, 0, 0);
44178         }.bind(this);
44179         
44180         img.src = img_src;
44181         
44182         this.is_empty = false;
44183     },
44184     
44185     selectImage: function()
44186     {
44187         this.fileEl().dom.click();
44188     },
44189     
44190     uploadImage: function(e)
44191     {
44192         var reader = new FileReader();
44193         
44194         reader.onload = function(e){
44195             var img = new Image();
44196             img.onload = function(){
44197                 this.reset();
44198                 this.canvasElCtx().drawImage(img, 0, 0);
44199             }.bind(this);
44200             img.src = e.target.result;
44201         }.bind(this);
44202         
44203         reader.readAsDataURL(e.target.files[0]);
44204     },
44205     
44206     // Bezier Point Constructor
44207     Point: (function () {
44208         function Point(x, y, time) {
44209             this.x = x;
44210             this.y = y;
44211             this.time = time || Date.now();
44212         }
44213         Point.prototype.distanceTo = function (start) {
44214             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44215         };
44216         Point.prototype.equals = function (other) {
44217             return this.x === other.x && this.y === other.y && this.time === other.time;
44218         };
44219         Point.prototype.velocityFrom = function (start) {
44220             return this.time !== start.time
44221             ? this.distanceTo(start) / (this.time - start.time)
44222             : 0;
44223         };
44224         return Point;
44225     }()),
44226     
44227     
44228     // Bezier Constructor
44229     Bezier: (function () {
44230         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44231             this.startPoint = startPoint;
44232             this.control2 = control2;
44233             this.control1 = control1;
44234             this.endPoint = endPoint;
44235             this.startWidth = startWidth;
44236             this.endWidth = endWidth;
44237         }
44238         Bezier.fromPoints = function (points, widths, scope) {
44239             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44240             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44241             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44242         };
44243         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44244             var dx1 = s1.x - s2.x;
44245             var dy1 = s1.y - s2.y;
44246             var dx2 = s2.x - s3.x;
44247             var dy2 = s2.y - s3.y;
44248             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44249             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44250             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44251             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44252             var dxm = m1.x - m2.x;
44253             var dym = m1.y - m2.y;
44254             var k = l2 / (l1 + l2);
44255             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44256             var tx = s2.x - cm.x;
44257             var ty = s2.y - cm.y;
44258             return {
44259                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44260                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44261             };
44262         };
44263         Bezier.prototype.length = function () {
44264             var steps = 10;
44265             var length = 0;
44266             var px;
44267             var py;
44268             for (var i = 0; i <= steps; i += 1) {
44269                 var t = i / steps;
44270                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44271                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44272                 if (i > 0) {
44273                     var xdiff = cx - px;
44274                     var ydiff = cy - py;
44275                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44276                 }
44277                 px = cx;
44278                 py = cy;
44279             }
44280             return length;
44281         };
44282         Bezier.prototype.point = function (t, start, c1, c2, end) {
44283             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44284             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44285             + (3.0 * c2 * (1.0 - t) * t * t)
44286             + (end * t * t * t);
44287         };
44288         return Bezier;
44289     }()),
44290     
44291     throttleStroke: function(fn, wait) {
44292       if (wait === void 0) { wait = 250; }
44293       var previous = 0;
44294       var timeout = null;
44295       var result;
44296       var storedContext;
44297       var storedArgs;
44298       var later = function () {
44299           previous = Date.now();
44300           timeout = null;
44301           result = fn.apply(storedContext, storedArgs);
44302           if (!timeout) {
44303               storedContext = null;
44304               storedArgs = [];
44305           }
44306       };
44307       return function wrapper() {
44308           var args = [];
44309           for (var _i = 0; _i < arguments.length; _i++) {
44310               args[_i] = arguments[_i];
44311           }
44312           var now = Date.now();
44313           var remaining = wait - (now - previous);
44314           storedContext = this;
44315           storedArgs = args;
44316           if (remaining <= 0 || remaining > wait) {
44317               if (timeout) {
44318                   clearTimeout(timeout);
44319                   timeout = null;
44320               }
44321               previous = now;
44322               result = fn.apply(storedContext, storedArgs);
44323               if (!timeout) {
44324                   storedContext = null;
44325                   storedArgs = [];
44326               }
44327           }
44328           else if (!timeout) {
44329               timeout = window.setTimeout(later, remaining);
44330           }
44331           return result;
44332       };
44333   }
44334   
44335 });
44336
44337  
44338
44339